init secondary color to (0,0,0,1). remove some redundant initializations.
[mesa.git] / src / mesa / main / program.c
index 07ab420bcf7bcc35f7a55215bff09e7ac72e9b80..e32e8a3b1aced79aa8b0717ebb664d0147e7946d 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  5.1
+ * Version:  6.0
  *
- * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ * Copyright (C) 1999-2004  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"),
@@ -36,6 +36,9 @@
 #include "macros.h"
 #include "mtypes.h"
 #include "program.h"
+#include "nvfragparse.h"
+#include "nvfragprog.h"
+#include "nvvertparse.h"
 
 
 /**********************************************************************/
@@ -137,14 +140,17 @@ _mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
 
 
 /**
- * Allocate and initialize a new fragment/vertex program object
+ * Allocate and initialize a new fragment/vertex program object but don't
+ * put it into the program hash table.
+ * Called via ctx->Driver.NewProgram.  May be wrapped (OO deriviation)
+ * by a device driver function.
  * \param ctx  context
  * \param id   program id/number
  * \param target  program target/type
  * \return  pointer to new program object
  */
 struct program *
-_mesa_alloc_program(GLcontext *ctx, GLenum target, GLuint id)
+_mesa_new_program(GLcontext *ctx, GLenum target, GLuint id)
 {
    struct program *prog;
 
@@ -165,7 +171,7 @@ _mesa_alloc_program(GLcontext *ctx, GLenum target, GLuint id)
       prog = &(fprog->Base);
    }
    else {
-      _mesa_problem(ctx, "bad target in _mesa_alloc_program");
+      _mesa_problem(ctx, "bad target in _mesa_new_program");
       return NULL;
    }
    prog->Id = id;
@@ -179,7 +185,8 @@ _mesa_alloc_program(GLcontext *ctx, GLenum target, GLuint id)
 /**
  * Delete a program and remove it from the hash table, ignoring the
  * reference count.
- * \note Called from the GL API dispatcher.
+ * Called via ctx->Driver.DeleteProgram.  May be wrapped (OO deriviation)
+ * by a device driver function.
  */
 void
 _mesa_delete_program(GLcontext *ctx, struct program *prog)
@@ -194,7 +201,8 @@ _mesa_delete_program(GLcontext *ctx, struct program *prog)
       if (vprog->Instructions)
          _mesa_free(vprog->Instructions);
    }
-   else if (prog->Target == GL_FRAGMENT_PROGRAM_NV) {
+   else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
+            prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
       struct fragment_program *fprog = (struct fragment_program *) prog;
       if (fprog->Instructions)
          _mesa_free(fprog->Instructions);
@@ -257,9 +265,10 @@ add_parameter(struct program_parameter_list *paramList,
 {
    const GLuint n = paramList->NumParameters;
 
-   paramList->Parameters = _mesa_realloc(paramList->Parameters,
-                                   n * sizeof(struct program_parameter),
-                                   (n + 1) * sizeof(struct program_parameter));
+   paramList->Parameters = (struct program_parameter *)
+      _mesa_realloc(paramList->Parameters,
+                    n * sizeof(struct program_parameter),
+                    (n + 1) * sizeof(struct program_parameter));
    if (!paramList->Parameters) {
       /* out of memory */
       paramList->NumParameters = 0;
@@ -325,17 +334,25 @@ _mesa_add_unnamed_constant(struct program_parameter_list *paramList,
 /**
  * Add a new state reference to the parameter list.
  * \param paramList - the parameter list
- * \param values - four float values
+ * \param state     - an array of 6 state tokens
+ *
  * \return index of the new parameter.
  */
 GLint
 _mesa_add_state_reference(struct program_parameter_list *paramList,
-                          const char *stateString)
+                          GLint *stateTokens)
 {
    /* XXX Should we parse <stateString> here and produce the parameter's
     * list of STATE_* tokens here, or in the parser?
     */
-   return add_parameter(paramList, stateString, NULL, STATE);
+   GLint a, idx;
+
+   idx = add_parameter(paramList, "Some State", NULL, STATE);
+       
+   for (a=0; a<6; a++)
+      paramList->Parameters[idx].StateIndexes[a] = (enum state_index) stateTokens[a];
+
+   return idx;
 }
 
 
@@ -360,7 +377,7 @@ _mesa_lookup_parameter_value(struct program_parameter_list *paramList,
       /* name is not null-terminated, use nameLen */
       for (i = 0; i < paramList->NumParameters; i++) {
          if (_mesa_strncmp(paramList->Parameters[i].Name, name, nameLen) == 0
-             && _mesa_strlen(paramList->Parameters[i].Name) == nameLen)
+             && _mesa_strlen(paramList->Parameters[i].Name) == (size_t)nameLen)
             return paramList->Parameters[i].Values;
       }
    }
@@ -389,7 +406,7 @@ _mesa_lookup_parameter_index(struct program_parameter_list *paramList,
       /* name is not null-terminated, use nameLen */
       for (i = 0; i < (GLint) paramList->NumParameters; i++) {
          if (_mesa_strncmp(paramList->Parameters[i].Name, name, nameLen) == 0
-             && _mesa_strlen(paramList->Parameters[i].Name) == nameLen)
+             && _mesa_strlen(paramList->Parameters[i].Name) == (size_t)nameLen)
             return i;
       }
    }
@@ -481,6 +498,21 @@ _mesa_fetch_state(GLcontext *ctx, const enum state_index state[],
          case STATE_SPOT_DIRECTION:
             COPY_4V(value, ctx->Light.Light[ln].EyeDirection);
             return;
+         case STATE_HALF:
+            {
+               GLfloat eye_z[] = {0, 0, 1};
+                                       
+               /* Compute infinite half angle vector:
+                *   half-vector = light_position + (0, 0, 1) 
+                * and then normalize.  w = 0
+                                        *
+                                        * light.EyePosition.w should be 0 for infinite lights.
+                */
+                                       ADD_3V(value, eye_z, ctx->Light.Light[ln].EyePosition);
+                                       NORMALIZE_3FV(value);
+                                       value[3] = 0;
+            }                                            
+            return;
          default:
             _mesa_problem(ctx, "Invalid light state in fetch_state");
             return;
@@ -583,6 +615,13 @@ _mesa_fetch_state(GLcontext *ctx, const enum state_index state[],
          }
       }
       return;
+   case STATE_TEXENV_COLOR:
+      {                
+         /* state[1] is the texture unit */
+         const GLuint unit = (GLuint) state[1];
+         COPY_4V(value, ctx->Texture.Unit[unit].EnvColor);
+      }                        
+      return;
    case STATE_FOG_COLOR:
       COPY_4V(value, ctx->Fog.Color);
       return;
@@ -590,7 +629,7 @@ _mesa_fetch_state(GLcontext *ctx, const enum state_index state[],
       value[0] = ctx->Fog.Density;
       value[1] = ctx->Fog.Start;
       value[2] = ctx->Fog.End;
-      value[3] = 1.0F / (ctx->Fog.End - ctx->Fog.End);
+      value[3] = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
       return;
    case STATE_CLIPPLANE:
       {
@@ -672,6 +711,53 @@ _mesa_fetch_state(GLcontext *ctx, const enum state_index state[],
          }
       }
       return;
+   case STATE_DEPTH_RANGE:
+      value[0] = ctx->Viewport.Near;                     /* near       */              
+      value[1] = ctx->Viewport.Far;                      /* far        */              
+      value[2] = ctx->Viewport.Far - ctx->Viewport.Near; /* far - near */              
+      value[3] = 0;            
+      return;
+   case STATE_FRAGMENT_PROGRAM:
+      {
+         /* state[1] = {STATE_ENV, STATE_LOCAL} */
+         /* state[2] = parameter index          */
+         int idx = state[2];                             
+
+         switch (state[1]) {
+            case STATE_ENV:
+               COPY_4V(value, ctx->FragmentProgram.Parameters[idx]);                                             
+               break;
+
+            case STATE_LOCAL:
+               COPY_4V(value, ctx->FragmentProgram.Current->Base.LocalParams[idx]);
+               break;                          
+            default:
+               _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()");
+               return;
+         }                               
+      }                        
+      return;
+               
+   case STATE_VERTEX_PROGRAM:
+      {                
+         /* state[1] = {STATE_ENV, STATE_LOCAL} */
+         /* state[2] = parameter index          */
+         int idx = state[2];                             
+                       
+         switch (state[1]) {
+            case STATE_ENV:
+               COPY_4V(value, ctx->VertexProgram.Parameters[idx]);                                               
+               break;
+
+            case STATE_LOCAL:
+               COPY_4V(value, ctx->VertexProgram.Current->Base.LocalParams[idx]);
+               break;                          
+            default:
+               _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()");
+               return;
+         }                               
+      }                        
+      return;
    default:
       _mesa_problem(ctx, "Invalid state in fetch_state");
       return;
@@ -710,13 +796,15 @@ _mesa_load_state_parameters(GLcontext *ctx,
  * \note Called from the GL API dispatcher by both glBindProgramNV
  * and glBindProgramARB.
  */
-void
+void GLAPIENTRY
 _mesa_BindProgram(GLenum target, GLuint id)
 {
    struct program *prog;
    GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END(ctx);
 
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
    if ((target == GL_VERTEX_PROGRAM_NV
         && ctx->Extensions.NV_vertex_program) ||
        (target == GL_VERTEX_PROGRAM_ARB
@@ -729,7 +817,7 @@ _mesa_BindProgram(GLenum target, GLuint id)
          ctx->VertexProgram.Current->Base.RefCount--;
          /* and delete if refcount goes below one */
          if (ctx->VertexProgram.Current->Base.RefCount <= 0) {
-            _mesa_delete_program(ctx, &(ctx->VertexProgram.Current->Base));
+            ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base));
             _mesa_HashRemove(ctx->Shared->Programs, id);
          }
       }
@@ -746,7 +834,7 @@ _mesa_BindProgram(GLenum target, GLuint id)
          ctx->FragmentProgram.Current->Base.RefCount--;
          /* and delete if refcount goes below one */
          if (ctx->FragmentProgram.Current->Base.RefCount <= 0) {
-            _mesa_delete_program(ctx, &(ctx->FragmentProgram.Current->Base));
+            ctx->Driver.DeleteProgram(ctx, &(ctx->FragmentProgram.Current->Base));
             _mesa_HashRemove(ctx->Shared->Programs, id);
          }
       }
@@ -782,7 +870,7 @@ _mesa_BindProgram(GLenum target, GLuint id)
       }
       else {
          /* allocate a new program now */
-         prog = _mesa_alloc_program(ctx, target, id);
+         prog = ctx->Driver.NewProgram(ctx, target, id);
          if (!prog) {
             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB");
             return;
@@ -805,6 +893,9 @@ _mesa_BindProgram(GLenum target, GLuint id)
 
    if (prog)
       prog->RefCount++;
+
+   if (ctx->Driver.BindProgram)
+      ctx->Driver.BindProgram(ctx, target, prog);
 }
 
 
@@ -813,12 +904,12 @@ _mesa_BindProgram(GLenum target, GLuint id)
  * \note Not compiled into display lists.
  * \note Called by both glDeleteProgramsNV and glDeleteProgramsARB.
  */
-void
+void GLAPIENTRY 
 _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
 {
    GLint i;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
    if (n < 0) {
       _mesa_error( ctx, GL_INVALID_VALUE, "glDeleteProgramsNV" );
@@ -838,7 +929,8 @@ _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
                   _mesa_BindProgram(prog->Target, 0);
                }
             }
-            else if (prog->Target == GL_FRAGMENT_PROGRAM_NV) {
+            else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
+                     prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
                if (ctx->FragmentProgram.Current &&
                    ctx->FragmentProgram.Current->Base.Id == ids[i]) {
                   /* unbind this currently bound program */
@@ -851,7 +943,7 @@ _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
             }
             prog->RefCount--;
             if (prog->RefCount <= 0) {
-               _mesa_delete_program(ctx, prog);
+               ctx->Driver.DeleteProgram(ctx, prog);
             }
          }
       }
@@ -864,7 +956,7 @@ _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
  * \note Not compiled into display lists.
  * \note Called by both glGenProgramsNV and glGenProgramsARB.
  */
-void
+void GLAPIENTRY
 _mesa_GenPrograms(GLsizei n, GLuint *ids)
 {
    GLuint first;
@@ -909,7 +1001,7 @@ _mesa_GenPrograms(GLsizei n, GLuint *ids)
  * \param id is the program identifier
  * \return GL_TRUE if id is a program, else GL_FALSE.
  */
-GLboolean
+GLboolean GLAPIENTRY
 _mesa_IsProgram(GLuint id)
 {
    struct program *prog;
@@ -925,3 +1017,225 @@ _mesa_IsProgram(GLuint id)
    else
       return GL_FALSE;
 }
+
+
+
+/**********************************************************************/
+/* GL_MESA_program_debug extension                                    */
+/**********************************************************************/
+
+
+/* XXX temporary */
+void
+glProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
+                      GLvoid *data)
+{
+   _mesa_ProgramCallbackMESA(target, callback, data);
+}
+
+
+void
+_mesa_ProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
+                          GLvoid *data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   switch (target) {
+      case GL_FRAGMENT_PROGRAM_ARB:
+         if (!ctx->Extensions.ARB_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->FragmentProgram.Callback = callback;
+         ctx->FragmentProgram.CallbackData = data;
+         break;
+      case GL_FRAGMENT_PROGRAM_NV:
+         if (!ctx->Extensions.NV_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->FragmentProgram.Callback = callback;
+         ctx->FragmentProgram.CallbackData = data;
+         break;
+      case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
+         if (!ctx->Extensions.ARB_vertex_program &&
+             !ctx->Extensions.NV_vertex_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->VertexProgram.Callback = callback;
+         ctx->VertexProgram.CallbackData = data;
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+         return;
+   }
+}
+
+
+/* XXX temporary */
+void
+glGetProgramRegisterfvMESA(GLenum target,
+                           GLsizei len, const GLubyte *registerName,
+                           GLfloat *v)
+{
+   _mesa_GetProgramRegisterfvMESA(target, len, registerName, v);
+}
+
+
+void
+_mesa_GetProgramRegisterfvMESA(GLenum target,
+                               GLsizei len, const GLubyte *registerName,
+                               GLfloat *v)
+{
+   char reg[1000];
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* We _should_ be inside glBegin/glEnd */
+#if 0
+   if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramRegisterfvMESA");
+      return;
+   }
+#endif
+
+   /* make null-terminated copy of registerName */
+   len = MIN2((unsigned int) len, sizeof(reg) - 1);
+   _mesa_memcpy(reg, registerName, len);
+   reg[len] = 0;
+
+   switch (target) {
+      case GL_VERTEX_PROGRAM_NV:
+         if (!ctx->Extensions.ARB_vertex_program &&
+             !ctx->Extensions.NV_vertex_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->VertexProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         /* GL_NV_vertex_program */
+         if (reg[0] == 'R') {
+            /* Temp register */
+            GLint i = _mesa_atoi(reg + 1);
+            if (i >= (GLint)ctx->Const.MaxVertexProgramTemps) {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+            COPY_4V(v, ctx->VertexProgram.Temporaries[i]);
+         }
+         else if (reg[0] == 'v' && reg[1] == '[') {
+            /* Vertex Input attribute */
+            GLuint i;
+            for (i = 0; i < ctx->Const.MaxVertexProgramAttribs; i++) {
+               const char *name = _mesa_nv_vertex_input_register_name(i);
+               char number[10];
+               sprintf(number, "%d", i);
+               if (_mesa_strncmp(reg + 2, name, 4) == 0 ||
+                   _mesa_strncmp(reg + 2, number, _mesa_strlen(number)) == 0) {
+                  COPY_4V(v, ctx->VertexProgram.Inputs[i]);
+                  return;
+               }
+            }
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         else if (reg[0] == 'o' && reg[1] == '[') {
+            /* Vertex output attribute */
+         }
+         /* GL_ARB_vertex_program */
+         else if (_mesa_strncmp(reg, "vertex.", 7) == 0) {
+
+         }
+         else {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         break;
+      case GL_FRAGMENT_PROGRAM_ARB:
+         if (!ctx->Extensions.ARB_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->FragmentProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         /* XXX to do */
+         break;
+      case GL_FRAGMENT_PROGRAM_NV:
+         if (!ctx->Extensions.NV_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->FragmentProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         if (reg[0] == 'R') {
+            /* Temp register */
+            GLint i = _mesa_atoi(reg + 1);
+            if (i >= (GLint)ctx->Const.MaxFragmentProgramTemps) {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+            COPY_4V(v, ctx->FragmentProgram.Machine.Temporaries[i]);
+         }
+         else if (reg[0] == 'f' && reg[1] == '[') {
+            /* Fragment input attribute */
+            GLuint i;
+            for (i = 0; i < ctx->Const.MaxFragmentProgramAttribs; i++) {
+               const char *name = _mesa_nv_fragment_input_register_name(i);
+               if (_mesa_strncmp(reg + 2, name, 4) == 0) {
+                  COPY_4V(v, ctx->FragmentProgram.Machine.Inputs[i]);
+                  return;
+               }
+            }
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         else if (_mesa_strcmp(reg, "o[COLR]") == 0) {
+            /* Fragment output color */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_COLR]);
+         }
+         else if (_mesa_strcmp(reg, "o[COLH]") == 0) {
+            /* Fragment output color */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_COLH]);
+         }
+         else if (_mesa_strcmp(reg, "o[DEPR]") == 0) {
+            /* Fragment output depth */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_DEPR]);
+         }
+         else {
+            /* try user-defined identifiers */
+            const GLfloat *value = _mesa_lookup_parameter_value(
+                       ctx->FragmentProgram.Current->Parameters, -1, reg);
+            if (value) {
+               COPY_4V(v, value);
+            }
+            else {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+         }
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glGetProgramRegisterfvMESA(target)");
+         return;
+   }
+
+}