/*
 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
 * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
 * 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 (including the
 * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
 * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS 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.
 */

#include <stdio.h>
#include <math.h>

#include "main/glheader.h"
#include "main/context.h"
#include "main/mtypes.h"
#include "main/macros.h"
#include "main/colormac.h"
#include "main/enums.h"

#include "swrast/swrast.h"
#include "swrast_setup/swrast_setup.h"
#include "tnl/t_context.h"
#include "tnl/t_pipeline.h"

#include "via_context.h"
#include "via_tris.h"
#include "via_state.h"
#include "via_span.h"
#include "via_ioctl.h"
#include "via_3d_reg.h"
#include "via_tex.h"

/***********************************************************************
 *                    Emit primitives as inline vertices               *
 ***********************************************************************/
#define LINE_FALLBACK (0)
#define POINT_FALLBACK (0)
#define TRI_FALLBACK (0)
#define ANY_FALLBACK_FLAGS (POINT_FALLBACK|LINE_FALLBACK|TRI_FALLBACK)
#define ANY_RASTER_FLAGS (DD_TRI_LIGHT_TWOSIDE|DD_TRI_OFFSET|DD_TRI_UNFILLED)


#if 0
#define COPY_DWORDS(vb, vertsize, v) 		\
do {						\
   via_sse_memcpy(vb, v, vertsize * 4);		\
   vb += vertsize;				\
} while (0)
#else
#if defined( USE_X86_ASM )
#define COPY_DWORDS(vb, vertsize, v)					\
    do {								\
        int j;								\
        int __tmp;							\
        __asm__ __volatile__("rep ; movsl"				\
                              : "=%c" (j), "=D" (vb), "=S" (__tmp)	\
                              : "0" (vertsize),				\
                                "D" ((long)vb),				\
                                "S" ((long)v));				\
    } while (0)
#else
#define COPY_DWORDS(vb, vertsize, v)		\
    do {					\
        int j;					\
        for (j = 0; j < vertsize; j++)		\
            vb[j] = ((GLuint *)v)[j];		\
        vb += vertsize;				\
    } while (0)
#endif
#endif

static void via_draw_triangle(struct via_context *vmesa,
			      viaVertexPtr v0,
			      viaVertexPtr v1,
			      viaVertexPtr v2)
{
   GLuint vertsize = vmesa->vertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 3 * 4 * vertsize);

   COPY_DWORDS(vb, vertsize, v0);
   COPY_DWORDS(vb, vertsize, v1);
   COPY_DWORDS(vb, vertsize, v2);
}


static void via_draw_quad(struct via_context *vmesa,
			  viaVertexPtr v0,
			  viaVertexPtr v1,
			  viaVertexPtr v2,
			  viaVertexPtr v3)
{
   GLuint vertsize = vmesa->vertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 6 * 4 * vertsize);

   COPY_DWORDS(vb, vertsize, v0);
   COPY_DWORDS(vb, vertsize, v1);
   COPY_DWORDS(vb, vertsize, v3);
   COPY_DWORDS(vb, vertsize, v1);
   COPY_DWORDS(vb, vertsize, v2);
   COPY_DWORDS(vb, vertsize, v3);
}

static void via_draw_line(struct via_context *vmesa,
			  viaVertexPtr v0,
			  viaVertexPtr v1)
{
   GLuint vertsize = vmesa->vertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 2 * 4 * vertsize);
   COPY_DWORDS(vb, vertsize, v0);
   COPY_DWORDS(vb, vertsize, v1);
}


static void via_draw_point(struct via_context *vmesa,
			   viaVertexPtr v0)
{
   GLuint vertsize = vmesa->vertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 4 * vertsize);
   COPY_DWORDS(vb, vertsize, v0);
}


/* Fallback drawing functions for the ptex hack.
 */
#define PTEX_VERTEX( tmp, vertex_size, v)	\
do {							\
   GLuint j;						\
   GLfloat rhw = 1.0 / v->f[vertex_size];		\
   for ( j = 0 ; j < vertex_size ; j++ )		\
      tmp.f[j] = v->f[j];				\
   tmp.f[3] *= v->f[vertex_size];			\
   tmp.f[vertex_size-2] *= rhw;				\
   tmp.f[vertex_size-1] *= rhw;				\
} while (0)

static void via_ptex_tri (struct via_context *vmesa,
			  viaVertexPtr v0,
			  viaVertexPtr v1,
			  viaVertexPtr v2)
{
   GLuint vertsize = vmesa->hwVertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 3*4*vertsize);
   viaVertex tmp;

   PTEX_VERTEX(tmp, vertsize, v0); COPY_DWORDS(vb, vertsize, &tmp);
   PTEX_VERTEX(tmp, vertsize, v1); COPY_DWORDS(vb, vertsize, &tmp);
   PTEX_VERTEX(tmp, vertsize, v2); COPY_DWORDS(vb, vertsize, &tmp);
}

static void via_ptex_line (struct via_context *vmesa,
			   viaVertexPtr v0,
			   viaVertexPtr v1)
{
   GLuint vertsize = vmesa->hwVertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 2*4*vertsize);
   viaVertex tmp;

   PTEX_VERTEX(tmp, vertsize, v0); COPY_DWORDS(vb, vertsize, &tmp);
   PTEX_VERTEX(tmp, vertsize, v1); COPY_DWORDS(vb, vertsize, &tmp);
}

static void via_ptex_point (struct via_context *vmesa,
			    viaVertexPtr v0)
{
   GLuint vertsize = vmesa->hwVertexSize;
   GLuint *vb = viaExtendPrimitive(vmesa, 1*4*vertsize);
   viaVertex tmp;

   PTEX_VERTEX(tmp, vertsize, v0); COPY_DWORDS(vb, vertsize, &tmp);
}





/***********************************************************************
 *          Macros for via_dd_tritmp.h to draw basic primitives        *
 ***********************************************************************/

#define TRI(a, b, c)                                \
    do {                                            \
        if (DO_FALLBACK)                            \
            vmesa->drawTri(vmesa, a, b, c);         \
        else                                        \
            via_draw_triangle(vmesa, a, b, c);      \
    } while (0)

#define QUAD(a, b, c, d)                            \
    do {                                            \
        if (DO_FALLBACK) {                          \
            vmesa->drawTri(vmesa, a, b, d);         \
            vmesa->drawTri(vmesa, b, c, d);         \
        }                                           \
        else                                        \
            via_draw_quad(vmesa, a, b, c, d);       \
    } while (0)

#define LINE(v0, v1)                                \
    do {                                            \
        if (DO_FALLBACK)                            \
            vmesa->drawLine(vmesa, v0, v1);         \
        else                                        \
            via_draw_line(vmesa, v0, v1);           \
    } while (0)

#define POINT(v0)                                    \
    do {                                             \
        if (DO_FALLBACK)                             \
            vmesa->drawPoint(vmesa, v0);             \
        else                                         \
            via_draw_point(vmesa, v0);               \
    } while (0)


/***********************************************************************
 *              Build render functions from dd templates               *
 ***********************************************************************/

#define VIA_OFFSET_BIT         0x01
#define VIA_TWOSIDE_BIT        0x02
#define VIA_UNFILLED_BIT       0x04
#define VIA_FALLBACK_BIT       0x08
#define VIA_MAX_TRIFUNC        0x10


static struct {
    tnl_points_func          points;
    tnl_line_func            line;
    tnl_triangle_func        triangle;
    tnl_quad_func            quad;
} rast_tab[VIA_MAX_TRIFUNC + 1];


#define DO_FALLBACK (IND & VIA_FALLBACK_BIT)
#define DO_OFFSET   (IND & VIA_OFFSET_BIT)
#define DO_UNFILLED (IND & VIA_UNFILLED_BIT)
#define DO_TWOSIDE  (IND & VIA_TWOSIDE_BIT)
#define DO_FLAT      0
#define DO_TRI       1
#define DO_QUAD      1
#define DO_LINE      1
#define DO_POINTS    1
#define DO_FULL_QUAD 1

#define HAVE_RGBA         1
#define HAVE_SPEC         1
#define HAVE_BACK_COLORS  0
#define HAVE_HW_FLATSHADE 1
#define VERTEX            viaVertex
#define TAB               rast_tab

/* Only used to pull back colors into vertices (ie, we know color is
 * floating point).
 */
#define VIA_COLOR(dst, src)                     \
    do {                                        \
        dst[0] = src[2];                        \
        dst[1] = src[1];                        \
        dst[2] = src[0];                        \
        dst[3] = src[3];                        \
    } while (0)

#define VIA_SPEC(dst, src)                      \
    do {                                        \
        dst[0] = src[2];                        \
        dst[1] = src[1];                        \
        dst[2] = src[0];                        \
    } while (0)


#define DEPTH_SCALE vmesa->polygon_offset_scale
#define UNFILLED_TRI unfilled_tri
#define UNFILLED_QUAD unfilled_quad
#define VERT_X(_v) _v->v.x
#define VERT_Y(_v) _v->v.y
#define VERT_Z(_v) _v->v.z
#define AREA_IS_CCW(a) (a > 0)
#define GET_VERTEX(e) (vmesa->verts + (e * vmesa->vertexSize * sizeof(int)))

#define VERT_SET_RGBA( v, c )  					\
do {								\
   via_color_t *color = (via_color_t *)&((v)->ui[coloroffset]);	\
   UNCLAMPED_FLOAT_TO_UBYTE(color->red, (c)[0]);		\
   UNCLAMPED_FLOAT_TO_UBYTE(color->green, (c)[1]);		\
   UNCLAMPED_FLOAT_TO_UBYTE(color->blue, (c)[2]);		\
   UNCLAMPED_FLOAT_TO_UBYTE(color->alpha, (c)[3]);		\
} while (0)

#define VERT_COPY_RGBA( v0, v1 ) v0->ui[coloroffset] = v1->ui[coloroffset]

#define VERT_SET_SPEC( v, c )					\
do {								\
   if (specoffset) {						\
     via_color_t *color = (via_color_t *)&((v)->ui[specoffset]);	\
     UNCLAMPED_FLOAT_TO_UBYTE(color->red, (c)[0]);		\
     UNCLAMPED_FLOAT_TO_UBYTE(color->green, (c)[1]);		\
     UNCLAMPED_FLOAT_TO_UBYTE(color->blue, (c)[2]);		\
   }								\
} while (0)
#define VERT_COPY_SPEC( v0, v1 )			\
do {							\
   if (specoffset) {					\
      v0->ub4[specoffset][0] = v1->ub4[specoffset][0];	\
      v0->ub4[specoffset][1] = v1->ub4[specoffset][1];	\
      v0->ub4[specoffset][2] = v1->ub4[specoffset][2];	\
   }							\
} while (0)


#define VERT_SAVE_RGBA( idx )    color[idx] = v[idx]->ui[coloroffset]
#define VERT_RESTORE_RGBA( idx ) v[idx]->ui[coloroffset] = color[idx]
#define VERT_SAVE_SPEC( idx )    if (specoffset) spec[idx] = v[idx]->ui[specoffset]
#define VERT_RESTORE_SPEC( idx ) if (specoffset) v[idx]->ui[specoffset] = spec[idx]


#define LOCAL_VARS(n)                                                   \
    struct via_context *vmesa = VIA_CONTEXT(ctx);                             \
    GLuint color[n], spec[n];                                           \
    GLuint coloroffset = vmesa->coloroffset;              \
    GLuint specoffset = vmesa->specoffset;                       \
    (void)color; (void)spec; (void)coloroffset; (void)specoffset;


/***********************************************************************
 *                Helpers for rendering unfilled primitives            *
 ***********************************************************************/

static const GLenum hwPrim[GL_POLYGON + 2] = {
    GL_POINTS,
    GL_LINES,
    GL_LINES,
    GL_LINES,
    GL_TRIANGLES,
    GL_TRIANGLES,
    GL_TRIANGLES,
    GL_TRIANGLES,
    GL_TRIANGLES,
    GL_TRIANGLES,
    GL_POLYGON+1
};


#define RASTERIZE(x) viaRasterPrimitive( ctx, x, hwPrim[x] )
#define RENDER_PRIMITIVE vmesa->renderPrimitive
#define TAG(x) x
#define IND VIA_FALLBACK_BIT
#include "tnl_dd/t_dd_unfilled.h"
#undef IND
#undef RASTERIZE

/***********************************************************************
 *                      Generate GL render functions                   *
 ***********************************************************************/
#define RASTERIZE(x)

#define IND (0)
#define TAG(x) x
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_OFFSET_BIT)
#define TAG(x) x##_offset
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT)
#define TAG(x) x##_twoside
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_OFFSET_BIT)
#define TAG(x) x##_twoside_offset
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_UNFILLED_BIT)
#define TAG(x) x##_unfilled
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_OFFSET_BIT|VIA_UNFILLED_BIT)
#define TAG(x) x##_offset_unfilled
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_UNFILLED_BIT)
#define TAG(x) x##_twoside_unfilled
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_OFFSET_BIT|VIA_UNFILLED_BIT)
#define TAG(x) x##_twoside_offset_unfilled
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_FALLBACK_BIT)
#define TAG(x) x##_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_OFFSET_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_offset_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_twoside_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_OFFSET_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_twoside_offset_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_UNFILLED_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_unfilled_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_OFFSET_BIT|VIA_UNFILLED_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_offset_unfilled_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_UNFILLED_BIT|VIA_FALLBACK_BIT)
#define TAG(x) x##_twoside_unfilled_fallback
#include "tnl_dd/t_dd_tritmp.h"

#define IND (VIA_TWOSIDE_BIT|VIA_OFFSET_BIT|VIA_UNFILLED_BIT| \
             VIA_FALLBACK_BIT)
#define TAG(x) x##_twoside_offset_unfilled_fallback
#include "tnl_dd/t_dd_tritmp.h"


/* Catchall case for flat, separate specular triangles (via has flat
 * diffuse shading, but always does specular color with gouraud).
 */
#undef  DO_FALLBACK
#undef  DO_OFFSET
#undef  DO_UNFILLED
#undef  DO_TWOSIDE
#undef  DO_FLAT
#define DO_FALLBACK (0)
#define DO_OFFSET   (ctx->_TriangleCaps & DD_TRI_OFFSET)
#define DO_UNFILLED (ctx->_TriangleCaps & DD_TRI_UNFILLED)
#define DO_TWOSIDE  (ctx->_TriangleCaps & DD_TRI_LIGHT_TWOSIDE)
#define DO_FLAT     1
#define TAG(x) x##_flat_specular
#define IND VIA_MAX_TRIFUNC
#include "tnl_dd/t_dd_tritmp.h"


static void init_rast_tab(void)
{
    init();
    init_offset();
    init_twoside();
    init_twoside_offset();
    init_unfilled();
    init_offset_unfilled();
    init_twoside_unfilled();
    init_twoside_offset_unfilled();
    init_fallback();
    init_offset_fallback();
    init_twoside_fallback();
    init_twoside_offset_fallback();
    init_unfilled_fallback();
    init_offset_unfilled_fallback();
    init_twoside_unfilled_fallback();
    init_twoside_offset_unfilled_fallback();

    init_flat_specular();	/* special! */
}


/***********************************************************************
 *                    Rasterization fallback helpers                   *
 ***********************************************************************/


/* This code is hit only when a mix of accelerated and unaccelerated
 * primitives are being drawn, and only for the unaccelerated
 * primitives.
 */
static void
via_fallback_tri(struct via_context *vmesa,
                 viaVertex *v0,
                 viaVertex *v1,
                 viaVertex *v2)
{    
    GLcontext *ctx = vmesa->glCtx;
    SWvertex v[3];
    _swsetup_Translate(ctx, v0, &v[0]);
    _swsetup_Translate(ctx, v1, &v[1]);
    _swsetup_Translate(ctx, v2, &v[2]);
    viaSpanRenderStart( ctx );
    _swrast_Triangle(ctx, &v[0], &v[1], &v[2]);
    viaSpanRenderFinish( ctx );
}


static void
via_fallback_line(struct via_context *vmesa,
                  viaVertex *v0,
                  viaVertex *v1)
{
    GLcontext *ctx = vmesa->glCtx;
    SWvertex v[2];
    _swsetup_Translate(ctx, v0, &v[0]);
    _swsetup_Translate(ctx, v1, &v[1]);
    viaSpanRenderStart( ctx );
    _swrast_Line(ctx, &v[0], &v[1]);
    viaSpanRenderFinish( ctx );
}


static void
via_fallback_point(struct via_context *vmesa,
                   viaVertex *v0)
{
    GLcontext *ctx = vmesa->glCtx;
    SWvertex v[1];
    _swsetup_Translate(ctx, v0, &v[0]);
    viaSpanRenderStart( ctx );
    _swrast_Point(ctx, &v[0]);
    viaSpanRenderFinish( ctx );
}

static void viaResetLineStipple( GLcontext *ctx )
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);
   vmesa->regCmdB |= HC_HLPrst_MASK;
}

/**********************************************************************/
/*               Render unclipped begin/end objects                   */
/**********************************************************************/
#define IND 0
#define V(x) (viaVertex *)(vertptr + ((x) * vertsize * sizeof(int)))
#define RENDER_POINTS(start, count)   \
    for (; start < count; start++) POINT(V(ELT(start)));
#define RENDER_LINE(v0, v1)         LINE(V(v0), V(v1))
#define RENDER_TRI( v0, v1, v2)     TRI( V(v0), V(v1), V(v2))
#define RENDER_QUAD(v0, v1, v2, v3) QUAD(V(v0), V(v1), V(v2), V(v3))
#define INIT(x) viaRasterPrimitive(ctx, x, hwPrim[x])
#undef LOCAL_VARS
#define LOCAL_VARS                                              \
    struct via_context *vmesa = VIA_CONTEXT(ctx);                     \
    GLubyte *vertptr = (GLubyte *)vmesa->verts;                 \
    const GLuint vertsize = vmesa->vertexSize;          \
    const GLuint * const elt = TNL_CONTEXT(ctx)->vb.Elts;       \
   const GLboolean stipple = ctx->Line.StippleFlag;		\
   (void) elt; (void) stipple;
#define RESET_STIPPLE	if ( stipple ) viaResetLineStipple( ctx );
#define RESET_OCCLUSION
#define PRESERVE_VB_DEFS
#define ELT(x) x
#define TAG(x) via_##x##_verts
#include "tnl/t_vb_rendertmp.h"
#undef ELT
#undef TAG
#define TAG(x) via_##x##_elts
#define ELT(x) elt[x]
#include "tnl/t_vb_rendertmp.h"
#undef ELT
#undef TAG
#undef NEED_EDGEFLAG_SETUP
#undef EDGEFLAG_GET
#undef EDGEFLAG_SET
#undef RESET_OCCLUSION


/**********************************************************************/
/*                   Render clipped primitives                        */
/**********************************************************************/



static void viaRenderClippedPoly(GLcontext *ctx, const GLuint *elts,
                                 GLuint n)
{
    TNLcontext *tnl = TNL_CONTEXT(ctx);
    struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;
    GLuint prim = VIA_CONTEXT(ctx)->renderPrimitive;

    /* Render the new vertices as an unclipped polygon.
     */
    {
        GLuint *tmp = VB->Elts;
        VB->Elts = (GLuint *)elts;
        tnl->Driver.Render.PrimTabElts[GL_POLYGON](ctx, 0, n,
                                                   PRIM_BEGIN|PRIM_END);
        VB->Elts = tmp;
    }

    /* Restore the render primitive
     */
    if (prim != GL_POLYGON &&
	prim != GL_POLYGON + 1)
       tnl->Driver.Render.PrimitiveNotify( ctx, prim );
}

static void viaRenderClippedLine(GLcontext *ctx, GLuint ii, GLuint jj)
{
    TNLcontext *tnl = TNL_CONTEXT(ctx);
    tnl->Driver.Render.Line(ctx, ii, jj);
}

static void viaFastRenderClippedPoly(GLcontext *ctx, const GLuint *elts,
                                     GLuint n)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    GLuint vertsize = vmesa->vertexSize;
    GLuint *vb = viaExtendPrimitive(vmesa, (n - 2) * 3 * 4 * vertsize);
    GLubyte *vertptr = (GLubyte *)vmesa->verts;
    const GLuint *start = (const GLuint *)V(elts[0]);
    int i;

    for (i = 2; i < n; i++) {
	COPY_DWORDS(vb, vertsize, V(elts[i - 1]));
        COPY_DWORDS(vb, vertsize, V(elts[i]));
	COPY_DWORDS(vb, vertsize, start);	
    }
}


/**********************************************************************/
/*                    Choose render functions                         */
/**********************************************************************/


#define _VIA_NEW_VERTEX (_NEW_TEXTURE |                         \
                         _DD_NEW_SEPARATE_SPECULAR |            \
                         _DD_NEW_TRI_UNFILLED |                 \
                         _DD_NEW_TRI_LIGHT_TWOSIDE |            \
                         _NEW_FOG)

#define _VIA_NEW_RENDERSTATE (_DD_NEW_LINE_STIPPLE |            \
                              _DD_NEW_TRI_UNFILLED |            \
                              _DD_NEW_TRI_LIGHT_TWOSIDE |       \
                              _DD_NEW_TRI_OFFSET |              \
                              _DD_NEW_TRI_STIPPLE |             \
                              _NEW_POLYGONSTIPPLE)


static void viaChooseRenderState(GLcontext *ctx)
{
   TNLcontext *tnl = TNL_CONTEXT(ctx);
   struct via_context *vmesa = VIA_CONTEXT(ctx);
   GLuint flags = ctx->_TriangleCaps;
   GLuint index = 0;

   if (vmesa->ptexHack) {
      vmesa->drawPoint = via_ptex_point;
      vmesa->drawLine = via_ptex_line;
      vmesa->drawTri = via_ptex_tri;
      index |= VIA_FALLBACK_BIT;
   }
   else {
      vmesa->drawPoint = via_draw_point;
      vmesa->drawLine = via_draw_line;
      vmesa->drawTri = via_draw_triangle;
   }

   if (flags & (ANY_FALLBACK_FLAGS | ANY_RASTER_FLAGS)) {
      if (ctx->Light.Enabled && ctx->Light.Model.TwoSide)
         index |= VIA_TWOSIDE_BIT;
      if (ctx->Polygon.FrontMode != GL_FILL || ctx->Polygon.BackMode != GL_FILL)
         index |= VIA_UNFILLED_BIT;
      if (flags & DD_TRI_OFFSET)
         index |= VIA_OFFSET_BIT;
      if (flags & ANY_FALLBACK_FLAGS)
         index |= VIA_FALLBACK_BIT;

      /* Hook in fallbacks for specific primitives. */
      if (flags & POINT_FALLBACK)
	 vmesa->drawPoint = via_fallback_point;
      
      if (flags & LINE_FALLBACK)
	 vmesa->drawLine = via_fallback_line;

      if (flags & TRI_FALLBACK)
	 vmesa->drawTri = via_fallback_tri;
   }

   if ((flags & DD_SEPARATE_SPECULAR) && ctx->Light.ShadeModel == GL_FLAT)
      index = VIA_MAX_TRIFUNC;	/* flat specular */

   if (vmesa->renderIndex != index) {
      vmesa->renderIndex = index;

      tnl->Driver.Render.Points = rast_tab[index].points;
      tnl->Driver.Render.Line = rast_tab[index].line;
      tnl->Driver.Render.Triangle = rast_tab[index].triangle;
      tnl->Driver.Render.Quad = rast_tab[index].quad;

      if (index == 0) {
	 tnl->Driver.Render.PrimTabVerts = via_render_tab_verts;
	 tnl->Driver.Render.PrimTabElts = via_render_tab_elts;
	 tnl->Driver.Render.ClippedLine = line; /* from tritmp.h */
	 tnl->Driver.Render.ClippedPolygon = viaFastRenderClippedPoly;
      }
      else {
	 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
	 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
	 tnl->Driver.Render.ClippedLine = viaRenderClippedLine;
	 tnl->Driver.Render.ClippedPolygon = viaRenderClippedPoly;
      }
   }
}


#define VIA_EMIT_TEX1	0x01
#define VIA_EMIT_TEX0	0x02
#define VIA_EMIT_PTEX0	0x04
#define VIA_EMIT_RGBA	0x08
#define VIA_EMIT_SPEC	0x10
#define VIA_EMIT_FOG	0x20
#define VIA_EMIT_W	0x40

#define EMIT_ATTR( ATTR, STYLE, INDEX, REGB )				\
do {									\
   vmesa->vertex_attrs[vmesa->vertex_attr_count].attrib = (ATTR);	\
   vmesa->vertex_attrs[vmesa->vertex_attr_count].format = (STYLE);	\
   vmesa->vertex_attr_count++;						\
   setupIndex |= (INDEX);						\
   regCmdB |= (REGB);							\
} while (0)

#define EMIT_PAD( N )							\
do {									\
   vmesa->vertex_attrs[vmesa->vertex_attr_count].attrib = 0;		\
   vmesa->vertex_attrs[vmesa->vertex_attr_count].format = EMIT_PAD;	\
   vmesa->vertex_attrs[vmesa->vertex_attr_count].offset = (N);		\
   vmesa->vertex_attr_count++;						\
} while (0)



static void viaChooseVertexState( GLcontext *ctx )
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);
   TNLcontext *tnl = TNL_CONTEXT(ctx);
   DECLARE_RENDERINPUTS(index_bitset);
   GLuint regCmdB = HC_HVPMSK_X | HC_HVPMSK_Y | HC_HVPMSK_Z;
   GLuint setupIndex = 0;

   RENDERINPUTS_COPY( index_bitset, tnl->render_inputs_bitset );
   vmesa->vertex_attr_count = 0;
 
   /* EMIT_ATTR's must be in order as they tell t_vertex.c how to
    * build up a hardware vertex.
    */
   if (RENDERINPUTS_TEST_RANGE( index_bitset, _TNL_FIRST_TEX, _TNL_LAST_TEX ) ||
       RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_FOG )) {
      EMIT_ATTR( _TNL_ATTRIB_POS, EMIT_4F_VIEWPORT, VIA_EMIT_W, HC_HVPMSK_W );
      vmesa->coloroffset = 4;
   }
   else {
      EMIT_ATTR( _TNL_ATTRIB_POS, EMIT_3F_VIEWPORT, 0, 0 );
      vmesa->coloroffset = 3;
   }

   /* t_context.c always includes a diffuse color */
   EMIT_ATTR( _TNL_ATTRIB_COLOR0, EMIT_4UB_4F_BGRA, VIA_EMIT_RGBA, 
	      HC_HVPMSK_Cd );
      
   vmesa->specoffset = 0;
   if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_COLOR1 ) ||
       RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_FOG )) {
      if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_COLOR1 )) {
	 vmesa->specoffset = vmesa->coloroffset + 1;
	 EMIT_ATTR( _TNL_ATTRIB_COLOR1, EMIT_3UB_3F_BGR, VIA_EMIT_SPEC, 
		    HC_HVPMSK_Cs );
      }
      else
	 EMIT_PAD( 3 );

      if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_FOG ))
	 EMIT_ATTR( _TNL_ATTRIB_FOG, EMIT_1UB_1F, VIA_EMIT_FOG, HC_HVPMSK_Cs );
      else
	 EMIT_PAD( 1 );
   }

   if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_TEX0 )) {
      if (vmesa->ptexHack)
	 EMIT_ATTR( _TNL_ATTRIB_TEX0, EMIT_3F_XYW, VIA_EMIT_PTEX0, 
		    (HC_HVPMSK_S | HC_HVPMSK_T) );
      else 
	 EMIT_ATTR( _TNL_ATTRIB_TEX0, EMIT_2F, VIA_EMIT_TEX0, 
		    (HC_HVPMSK_S | HC_HVPMSK_T) );
   }

   if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_TEX1 )) {
      EMIT_ATTR( _TNL_ATTRIB_TEX1, EMIT_2F, VIA_EMIT_TEX1, 
		 (HC_HVPMSK_S | HC_HVPMSK_T) );
   }

   if (setupIndex != vmesa->setupIndex) {
      vmesa->vertexSize = _tnl_install_attrs( ctx, 
					       vmesa->vertex_attrs, 
					       vmesa->vertex_attr_count,
					       vmesa->ViewportMatrix.m, 0 );
      vmesa->vertexSize >>= 2;
      vmesa->setupIndex = setupIndex;
      vmesa->regCmdB &= ~HC_HVPMSK_MASK;
      vmesa->regCmdB |= regCmdB;

      if (vmesa->ptexHack) 
	 vmesa->hwVertexSize = vmesa->vertexSize - 1;
      else
	 vmesa->hwVertexSize = vmesa->vertexSize;
   }
}




/* Check if projective texture coordinates are used and if we can fake
 * them. Fallback to swrast if we can't. Returns GL_TRUE if projective
 * texture coordinates must be faked, GL_FALSE otherwise.
 */
static GLboolean viaCheckPTexHack( GLcontext *ctx )
{
   TNLcontext *tnl = TNL_CONTEXT(ctx);
   struct vertex_buffer *VB = &tnl->vb;
   DECLARE_RENDERINPUTS(index_bitset);
   GLboolean fallback = GL_FALSE;
   GLboolean ptexHack = GL_FALSE;

   RENDERINPUTS_COPY( index_bitset, tnl->render_inputs_bitset );

   if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_TEX0 ) && VB->TexCoordPtr[0]->size == 4) {
      if (!RENDERINPUTS_TEST_RANGE( index_bitset, _TNL_ATTRIB_TEX1, _TNL_LAST_TEX ))
	 ptexHack = GL_TRUE; 
      else
	 fallback = GL_TRUE;
   }
   if (RENDERINPUTS_TEST( index_bitset, _TNL_ATTRIB_TEX1 ) && VB->TexCoordPtr[1]->size == 4)
      fallback = GL_TRUE;

   FALLBACK(VIA_CONTEXT(ctx), VIA_FALLBACK_PROJ_TEXTURE, fallback);
   return ptexHack;
}




/**********************************************************************/
/*                 High level hooks for t_vb_render.c                 */
/**********************************************************************/


static void viaRenderStart(GLcontext *ctx)
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);
   TNLcontext *tnl = TNL_CONTEXT(ctx);
   struct vertex_buffer *VB = &TNL_CONTEXT(ctx)->vb;

   {
      GLboolean ptexHack = viaCheckPTexHack( ctx );
      if (ptexHack != vmesa->ptexHack) {
	 vmesa->ptexHack = ptexHack;
	 vmesa->newRenderState |= _VIA_NEW_RENDERSTATE;
      }
   }

   if (vmesa->newState) {
      vmesa->newRenderState |= vmesa->newState;
      viaValidateState( ctx );
   }

   if (vmesa->Fallback) {
      tnl->Driver.Render.Start(ctx);
      return;
   }

   if (vmesa->newRenderState) {
      viaChooseVertexState(ctx);
      viaChooseRenderState(ctx);
      vmesa->newRenderState = 0;
   }

   /* Important:
    */
   VB->AttribPtr[VERT_ATTRIB_POS] = VB->NdcPtr;
}

static void viaRenderFinish(GLcontext *ctx)
{
   VIA_FINISH_PRIM(VIA_CONTEXT(ctx));
}


/* System to flush dma and emit state changes based on the rasterized
 * primitive.
 */
void viaRasterPrimitive(GLcontext *ctx,
			GLenum glprim,
			GLenum hwprim)
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);
   GLuint regCmdB;
   RING_VARS;

   if (VIA_DEBUG & DEBUG_PRIMS) 
      fprintf(stderr, "%s: %s/%s/%s\n", 
	      __FUNCTION__, _mesa_lookup_enum_by_nr(glprim),
	      _mesa_lookup_enum_by_nr(hwprim),
	      _mesa_lookup_enum_by_nr(ctx->Light.ShadeModel));

   assert (!vmesa->newState);

   vmesa->renderPrimitive = glprim;

   if (hwprim != vmesa->hwPrimitive ||
       ctx->Light.ShadeModel != vmesa->hwShadeModel) {

      VIA_FINISH_PRIM(vmesa);

      /* Ensure no wrapping inside this function  */    
      viaCheckDma( vmesa, 1024 );	

      if (vmesa->newEmitState) {
	 viaEmitState(vmesa);
      }
       
      vmesa->regCmdA_End = HC_ACMD_HCmdA;

      if (ctx->Light.ShadeModel == GL_SMOOTH) {
	 vmesa->regCmdA_End |= HC_HShading_Gouraud;
      }
      
      vmesa->hwShadeModel = ctx->Light.ShadeModel;
      regCmdB = vmesa->regCmdB;

      switch (hwprim) {
      case GL_POINTS:
	 vmesa->regCmdA_End |= HC_HPMType_Point | HC_HVCycle_Full;
	 vmesa->regCmdA_End |= HC_HShading_Gouraud; /* always Gouraud 
						       shade points?!? */
	 break;
      case GL_LINES:
	 vmesa->regCmdA_End |= HC_HPMType_Line | HC_HVCycle_Full;
         regCmdB |= HC_HLPrst_MASK;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatB; 
	 break;
      case GL_LINE_LOOP:
      case GL_LINE_STRIP:
	 vmesa->regCmdA_End |= HC_HPMType_Line | HC_HVCycle_AFP |
	    HC_HVCycle_AB | HC_HVCycle_NewB;
	 regCmdB |= HC_HVCycle_AB | HC_HVCycle_NewB | HC_HLPrst_MASK;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatB; 
	 break;
      case GL_TRIANGLES:
	 vmesa->regCmdA_End |= HC_HPMType_Tri | HC_HVCycle_Full;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatC; 
	 break;
      case GL_TRIANGLE_STRIP:
	 vmesa->regCmdA_End |= HC_HPMType_Tri | HC_HVCycle_AFP |
	    HC_HVCycle_AC | HC_HVCycle_BB | HC_HVCycle_NewC;
	 regCmdB |= HC_HVCycle_AA | HC_HVCycle_BC | HC_HVCycle_NewC;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatC; 
	 break;
      case GL_TRIANGLE_FAN:
	 vmesa->regCmdA_End |= HC_HPMType_Tri | HC_HVCycle_AFP |
	    HC_HVCycle_AA | HC_HVCycle_BC | HC_HVCycle_NewC;
	 regCmdB |= HC_HVCycle_AA | HC_HVCycle_BC | HC_HVCycle_NewC;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatC; 
	 break;
      case GL_QUADS:
	 abort();
	 return;
      case GL_QUAD_STRIP:
	 abort();
	 return;
      case GL_POLYGON:
	 vmesa->regCmdA_End |= HC_HPMType_Tri | HC_HVCycle_AFP |
	    HC_HVCycle_AA | HC_HVCycle_BC | HC_HVCycle_NewC;
	 regCmdB |= HC_HVCycle_AA | HC_HVCycle_BC | HC_HVCycle_NewC;
	 if (ctx->Light.ShadeModel == GL_FLAT)
            vmesa->regCmdA_End |= HC_HShading_FlatC; 
	 break;                          
      default:
	 abort();
	 return;
      }
    
/*     assert((vmesa->dmaLow & 0x4) == 0); */

      if (vmesa->dmaCliprectAddr == ~0) {
	 if (VIA_DEBUG & DEBUG_DMA) 
	    fprintf(stderr, "reserve cliprect space at %x\n", vmesa->dmaLow);
	 vmesa->dmaCliprectAddr = vmesa->dmaLow;
	 BEGIN_RING(8);
	 OUT_RING( HC_HEADER2 );    
	 OUT_RING( (HC_ParaType_NotTex << 16) );
	 OUT_RING( 0xCCCCCCCC );
	 OUT_RING( 0xCCCCCCCC );
	 OUT_RING( 0xCCCCCCCC );
	 OUT_RING( 0xCCCCCCCC );
	 OUT_RING( 0xCCCCCCCC );
	 OUT_RING( 0xCCCCCCCC );
	 ADVANCE_RING();
      }

      assert(vmesa->dmaLastPrim == 0);

      BEGIN_RING(8);
      OUT_RING( HC_HEADER2 );    
      OUT_RING( (HC_ParaType_NotTex << 16) );
      OUT_RING( 0xCCCCCCCC );
      OUT_RING( 0xDDDDDDDD );

      OUT_RING( HC_HEADER2 );    
      OUT_RING( (HC_ParaType_CmdVdata << 16) );
      OUT_RING( regCmdB );
      OUT_RING( vmesa->regCmdA_End );
      ADVANCE_RING();

      vmesa->hwPrimitive = hwprim;        
      vmesa->dmaLastPrim = vmesa->dmaLow;
   }
   else {
      assert(!vmesa->newEmitState);
   }
}

/* Callback for mesa:
 */
static void viaRenderPrimitive( GLcontext *ctx, GLuint prim )
{
   viaRasterPrimitive( ctx, prim, hwPrim[prim] );
}


void viaFinishPrimitive(struct via_context *vmesa)
{
   if (VIA_DEBUG & (DEBUG_DMA|DEBUG_PRIMS)) 
      fprintf(stderr, "%s\n", __FUNCTION__);

   if (!vmesa->dmaLastPrim || vmesa->dmaCliprectAddr == ~0) {
      assert(0);
   }
   else if (vmesa->dmaLow != vmesa->dmaLastPrim) {
      GLuint cmdA = (vmesa->regCmdA_End | HC_HPLEND_MASK | 
		     HC_HPMValidN_MASK | HC_HE3Fire_MASK); 
      RING_VARS;

      vmesa->dmaLastPrim = 0;

      /* KW: modified 0x1 to 0x4 below:
       */
      if ((vmesa->dmaLow & 0x4) || !vmesa->useAgp) {
	 BEGIN_RING_NOCHECK( 1 );
	 OUT_RING( cmdA );
	 ADVANCE_RING();
      }   
      else {      
	 BEGIN_RING_NOCHECK( 2 );
	 OUT_RING( cmdA );
	 OUT_RING( cmdA );
	 ADVANCE_RING();
      }   

      if (vmesa->dmaLow > VIA_DMA_HIGHWATER)
	 viaFlushDma( vmesa );
   }
   else {
      if (VIA_DEBUG & (DEBUG_DMA|DEBUG_PRIMS)) 
	 fprintf(stderr, "remove empty primitive\n");

      /* Remove the primitive header:
       */
      vmesa->dmaLastPrim = 0;
      vmesa->dmaLow -= 8 * sizeof(GLuint);

      /* Maybe remove the cliprect as well:
       */
      if (vmesa->dmaCliprectAddr == vmesa->dmaLow - 8 * sizeof(GLuint)) {
	 vmesa->dmaLow -= 8 * sizeof(GLuint);
	 vmesa->dmaCliprectAddr = ~0;
      }
   }

   vmesa->renderPrimitive = GL_POLYGON + 1;
   vmesa->hwPrimitive = GL_POLYGON + 1;
   vmesa->dmaLastPrim = 0;
}


/**********************************************************************/
/*           Transition to/from hardware rasterization.               */
/**********************************************************************/


void viaFallback(struct via_context *vmesa, GLuint bit, GLboolean mode)
{
    GLcontext *ctx = vmesa->glCtx;
    TNLcontext *tnl = TNL_CONTEXT(ctx);
    GLuint oldfallback = vmesa->Fallback;
    
    if (mode) {
        vmesa->Fallback |= bit;
        if (oldfallback == 0) {
	    VIA_FLUSH_DMA(vmesa);

 	    if (VIA_DEBUG & DEBUG_FALLBACKS) 
	       fprintf(stderr, "ENTER FALLBACK %x\n", bit);

            _swsetup_Wakeup(ctx);
            vmesa->renderIndex = ~0;
        }
    }
    else {
        vmesa->Fallback &= ~bit;
        if (oldfallback == bit) {
	    _swrast_flush( ctx );

 	    if (VIA_DEBUG & DEBUG_FALLBACKS) 
	       fprintf(stderr, "LEAVE FALLBACK %x\n", bit);

	    tnl->Driver.Render.Start = viaRenderStart;
            tnl->Driver.Render.PrimitiveNotify = viaRenderPrimitive;
            tnl->Driver.Render.Finish = viaRenderFinish;

	    tnl->Driver.Render.BuildVertices = _tnl_build_vertices;
	    tnl->Driver.Render.CopyPV = _tnl_copy_pv;
	    tnl->Driver.Render.Interp = _tnl_interp;
    	    tnl->Driver.Render.ResetLineStipple = viaResetLineStipple;

	    _tnl_invalidate_vertex_state( ctx, ~0 );
	    _tnl_invalidate_vertices( ctx, ~0 );
	    _tnl_install_attrs( ctx, 
				vmesa->vertex_attrs, 
				vmesa->vertex_attr_count,
				vmesa->ViewportMatrix.m, 0 ); 

            vmesa->newState |= (_VIA_NEW_RENDERSTATE|_VIA_NEW_VERTEX);
        }
    }    
}

static void viaRunPipeline( GLcontext *ctx )
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);

   if (vmesa->newState) {
      vmesa->newRenderState |= vmesa->newState;
      viaValidateState( ctx );
   }

   _tnl_run_pipeline( ctx );
}


/**********************************************************************/
/*                            Initialization.                         */
/**********************************************************************/


void viaInitTriFuncs(GLcontext *ctx)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    TNLcontext *tnl = TNL_CONTEXT(ctx);
    static int firsttime = 1;

    if (firsttime) {
        init_rast_tab();
        firsttime = 0;
    }

    tnl->Driver.RunPipeline = viaRunPipeline;
    tnl->Driver.Render.Start = viaRenderStart;
    tnl->Driver.Render.Finish = viaRenderFinish;
    tnl->Driver.Render.PrimitiveNotify = viaRenderPrimitive;
    tnl->Driver.Render.ResetLineStipple = viaResetLineStipple;
    tnl->Driver.Render.BuildVertices = _tnl_build_vertices;
    tnl->Driver.Render.CopyPV = _tnl_copy_pv;
    tnl->Driver.Render.Interp = _tnl_interp;

    _tnl_init_vertices( ctx, ctx->Const.MaxArrayLockSize + 12, 
			(6 + 2*ctx->Const.MaxTextureUnits) * sizeof(GLfloat) );
   
    vmesa->verts = (GLubyte *)tnl->clipspace.vertex_buf;

}
