/**************************************************************************
 * 
 * Copyright 2003 VMware, 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 VMWARE 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.
 * 
 **************************************************************************/

/*
 * Render unclipped vertex buffers by emitting vertices directly to
 * dma buffers.  Use strip/fan hardware acceleration where possible.
 *
 */
#include "main/glheader.h"
#include "main/context.h"
#include "main/macros.h"
#include "main/imports.h"
#include "main/mtypes.h"
#include "main/enums.h"

#include "math/m_xform.h"

#include "tnl/t_context.h"
#include "tnl/t_vertex.h"
#include "tnl/t_pipeline.h"

#include "intel_screen.h"
#include "intel_context.h"
#include "intel_tris.h"
#include "intel_batchbuffer.h"
#include "intel_reg.h"

/*
 * Render unclipped vertex buffers by emitting vertices directly to
 * dma buffers.  Use strip/fan hardware primitives where possible.
 * Try to simulate missing primitives with indexed vertices.
 */
#define HAVE_POINTS      1
#define HAVE_LINES       1
#define HAVE_LINE_STRIPS 1
#define HAVE_TRIANGLES   1
#define HAVE_TRI_STRIPS  1
#define HAVE_TRI_FANS    1
#define HAVE_POLYGONS    1

#define HAVE_ELTS        0

static const uint32_t hw_prim[GL_POLYGON + 1] = {
   [GL_POINTS] = PRIM3D_POINTLIST,
   [GL_LINES ] = PRIM3D_LINELIST,
   [GL_LINE_LOOP] = PRIM3D_LINESTRIP,
   [GL_LINE_STRIP] = PRIM3D_LINESTRIP,
   [GL_TRIANGLES] = PRIM3D_TRILIST,
   [GL_TRIANGLE_STRIP] = PRIM3D_TRISTRIP,
   [GL_TRIANGLE_FAN] = PRIM3D_TRIFAN,
   [GL_QUADS] = 0,
   [GL_QUAD_STRIP] = 0,
   [GL_POLYGON] = PRIM3D_POLY,
};

static const GLenum reduced_prim[GL_POLYGON + 1] = {
   [GL_POINTS] = GL_POINTS,
   [GL_LINES] = GL_LINES,
   [GL_LINE_LOOP] = GL_LINES,
   [GL_LINE_STRIP] = GL_LINES,
   [GL_TRIANGLES] = GL_TRIANGLES,
   [GL_TRIANGLE_STRIP] = GL_TRIANGLES,
   [GL_TRIANGLE_FAN] = GL_TRIANGLES,
   [GL_QUADS] = GL_TRIANGLES,
   [GL_QUAD_STRIP] = GL_TRIANGLES,
   [GL_POLYGON] = GL_TRIANGLES,
};

static const int scale_prim[GL_POLYGON + 1] = {
   [GL_POINTS] = 1,
   [GL_LINES] = 1,
   [GL_LINE_LOOP] = 2,
   [GL_LINE_STRIP] = 2,
   [GL_TRIANGLES] = 1,
   [GL_TRIANGLE_STRIP] = 3,
   [GL_TRIANGLE_FAN] = 3,
   [GL_QUADS] = 0,              /* fallback case */
   [GL_QUAD_STRIP] = 0,         /* fallback case */
   [GL_POLYGON] = 3,
};


static void
intelDmaPrimitive(struct intel_context *intel, GLenum prim)
{
   if (0)
      fprintf(stderr, "%s %s\n", __func__, _mesa_enum_to_string(prim));
   INTEL_FIREVERTICES(intel);
   intel->vtbl.reduced_primitive_state(intel, reduced_prim[prim]);
   intel_set_prim(intel, hw_prim[prim]);
}

#define INTEL_NO_VBO_STATE_RESERVED 1500

static inline GLuint intel_get_vb_max(struct intel_context *intel)
{
   GLuint ret;

   if (intel->intelScreen->no_vbo) {
      ret = intel->batch.bo->size - INTEL_NO_VBO_STATE_RESERVED;
   } else
      ret = INTEL_VB_SIZE;
   ret /= (intel->vertex_size * 4);
   return ret;
}

static inline GLuint intel_get_current_max(struct intel_context *intel)
{
   GLuint ret;

   if (intel->intelScreen->no_vbo) {
      ret = intel_batchbuffer_space(intel);
      ret = ret <= INTEL_NO_VBO_STATE_RESERVED ? 0 : ret - INTEL_NO_VBO_STATE_RESERVED;
   } else
      ret = (INTEL_VB_SIZE - intel->prim.current_offset);

   return ret / (intel->vertex_size * 4);
}

#define LOCAL_VARS struct intel_context *intel = intel_context(ctx)
#define INIT( prim ) 				\
do {						\
   intelDmaPrimitive( intel, prim );		\
} while (0)

#define FLUSH() INTEL_FIREVERTICES(intel)

#define GET_SUBSEQUENT_VB_MAX_VERTS() intel_get_vb_max(intel)
#define GET_CURRENT_VB_MAX_VERTS() intel_get_current_max(intel)

#define ALLOC_VERTS(nr) intel_get_prim_space(intel, nr)

#define EMIT_VERTS( ctx, j, nr, buf ) \
  _tnl_emit_vertices_to_buffer(ctx, j, (j)+(nr), buf )

#define TAG(x) intel_##x
#include "tnl_dd/t_dd_dmatmp.h"


/**********************************************************************/
/*                          Render pipeline stage                     */
/**********************************************************************/

/* Heuristic to choose between the two render paths:  
 */
static bool
choose_render(struct intel_context *intel, struct vertex_buffer *VB)
{
   int vertsz = intel->vertex_size;
   int cost_render = 0;
   int cost_fallback = 0;
   int nr_prims = 0;
   int nr_rprims = 0;
   int nr_rverts = 0;
   int rprim = intel->reduced_primitive;
   int i = 0;

   for (i = 0; i < VB->PrimitiveCount; i++) {
      GLuint prim = VB->Primitive[i].mode;
      GLuint length = VB->Primitive[i].count;

      if (!length)
         continue;

      nr_prims++;
      nr_rverts += length * scale_prim[prim & PRIM_MODE_MASK];

      if (reduced_prim[prim & PRIM_MODE_MASK] != rprim) {
         nr_rprims++;
         rprim = reduced_prim[prim & PRIM_MODE_MASK];
      }
   }

   /* One point for each generated primitive:
    */
   cost_render = nr_prims;
   cost_fallback = nr_rprims;

   /* One point for every 1024 dwords (4k) of dma:
    */
   cost_render += (vertsz * i) / 1024;
   cost_fallback += (vertsz * nr_rverts) / 1024;

   if (0)
      fprintf(stderr, "cost render: %d fallback: %d\n",
              cost_render, cost_fallback);

   if (cost_render > cost_fallback)
      return false;

   return true;
}


static GLboolean
intel_run_render(struct gl_context * ctx, struct tnl_pipeline_stage *stage)
{
   struct intel_context *intel = intel_context(ctx);
   TNLcontext *tnl = TNL_CONTEXT(ctx);
   struct vertex_buffer *VB = &tnl->vb;
   GLuint i;

   intel->vtbl.render_prevalidate( intel );

   /* Don't handle clipping or indexed vertices.
    */
   if (intel->RenderIndex != 0 ||
       !intel_validate_render(ctx, VB) || !choose_render(intel, VB)) {
      return true;
   }

   tnl->clipspace.new_inputs |= VERT_BIT_POS;

   tnl->Driver.Render.Start(ctx);

   for (i = 0; i < VB->PrimitiveCount; i++) {
      GLuint prim = _tnl_translate_prim(&VB->Primitive[i]);
      GLuint start = VB->Primitive[i].start;
      GLuint length = VB->Primitive[i].count;

      if (!length)
         continue;

      intel_render_tab_verts[prim & PRIM_MODE_MASK] (ctx, start,
                                                     length, prim);
   }

   tnl->Driver.Render.Finish(ctx);

   INTEL_FIREVERTICES(intel);

   return false;             /* finished the pipe */
}

static const struct tnl_pipeline_stage _intel_render_stage = {
   "intel render",
   NULL,
   NULL,
   NULL,
   NULL,
   intel_run_render             /* run */
};

const struct tnl_pipeline_stage *intel_pipeline[] = {
   &_tnl_vertex_transform_stage,
   &_tnl_normal_transform_stage,
   &_tnl_lighting_stage,
   &_tnl_fog_coordinate_stage,
   &_tnl_texgen_stage,
   &_tnl_texture_transform_stage,
   &_tnl_point_attenuation_stage,
   &_tnl_vertex_program_stage,
#if 1
   &_intel_render_stage,        /* ADD: unclipped rastersetup-to-dma */
#endif
   &_tnl_render_stage,
   0,
};
