Merge commit 'origin/master' into gallium-0.2
[mesa.git] / src / mesa / shader / slang / slang_link.c
index 79fd9a064f5f9bf373c2f2a8d47519cfa14c2b5f..3ff94d21a3398be40f920d4385dec04f51a7d623 100644 (file)
@@ -1,8 +1,9 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.2
+ * Version:  7.3
  *
  * Copyright (C) 2008  Brian Paul   All Rights Reserved.
+ * Copyright (C) 2009  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"),
@@ -75,43 +76,94 @@ link_error(struct gl_shader_program *shProg, const char *msg)
 
 
 
+/**
+ * Check if the given bit is either set or clear in both bitfields.
+ */
+static GLboolean
+bits_agree(GLbitfield flags1, GLbitfield flags2, GLbitfield bit)
+{
+   return (flags1 & bit) == (flags2 & bit);
+}
+
+
 /**
  * Linking varying vars involves rearranging varying vars so that the
  * vertex program's output varyings matches the order of the fragment
  * program's input varyings.
+ * We'll then rewrite instructions to replace PROGRAM_VARYING with either
+ * PROGRAM_INPUT or PROGRAM_OUTPUT depending on whether it's a vertex or
+ * fragment shader.
+ * This is also where we set program Input/OutputFlags to indicate
+ * which inputs are centroid-sampled, invariant, etc.
  */
 static GLboolean
 link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog)
 {
    GLuint *map, i, firstVarying, newFile;
+   GLbitfield *inOutFlags;
 
    map = (GLuint *) malloc(prog->Varying->NumParameters * sizeof(GLuint));
    if (!map)
       return GL_FALSE;
 
+   /* Varying variables are treated like other vertex program outputs
+    * (and like other fragment program inputs).  The position of the
+    * first varying differs for vertex/fragment programs...
+    * Also, replace File=PROGRAM_VARYING with File=PROGRAM_INPUT/OUTPUT.
+    */
+   if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
+      firstVarying = VERT_RESULT_VAR0;
+      newFile = PROGRAM_OUTPUT;
+      inOutFlags = prog->OutputFlags;
+   }
+   else {
+      assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
+      firstVarying = FRAG_ATTRIB_VAR0;
+      newFile = PROGRAM_INPUT;
+      inOutFlags = prog->InputFlags;
+   }
+
    for (i = 0; i < prog->Varying->NumParameters; i++) {
       /* see if this varying is in the linked varying list */
       const struct gl_program_parameter *var = prog->Varying->Parameters + i;
       GLint j = _mesa_lookup_parameter_index(shProg->Varying, -1, var->Name);
       if (j >= 0) {
-         /* already in list, check size */
-         if (var->Size != shProg->Varying->Parameters[j].Size) {
-            /* error */
+         /* varying is already in list, do some error checking */
+         const struct gl_program_parameter *v =
+            &shProg->Varying->Parameters[j];
+         if (var->Size != v->Size) {
             link_error(shProg, "mismatched varying variable types");
             return GL_FALSE;
          }
+         if (!bits_agree(var->Flags, v->Flags, PROG_PARAM_BIT_CENTROID)) {
+            char msg[100];
+            snprintf(msg, sizeof(msg),
+                     "centroid modifier mismatch for '%s'", var->Name);
+            link_error(shProg, msg);
+            return GL_FALSE;
+         }
+         if (!bits_agree(var->Flags, v->Flags, PROG_PARAM_BIT_INVARIANT)) {
+            char msg[100];
+            snprintf(msg, sizeof(msg),
+                     "invariant modifier mismatch for '%s'", var->Name);
+            link_error(shProg, msg);
+            return GL_FALSE;
+         }
       }
       else {
          /* not already in linked list */
-         j = _mesa_add_varying(shProg->Varying, var->Name, var->Size);
+         j = _mesa_add_varying(shProg->Varying, var->Name, var->Size,
+                               var->Flags);
       }
 
-      /* map varying[i] to varying[j].
+      /* Map varying[i] to varying[j].
+       * Plus, set prog->Input/OutputFlags[] as described above.
        * Note: the loop here takes care of arrays or large (sz>4) vars.
        */
       {
          GLint sz = var->Size;
          while (sz > 0) {
+            inOutFlags[firstVarying + j] = var->Flags;
             /*printf("Link varying from %d to %d\n", i, j);*/
             map[i++] = j++;
             sz -= 4;
@@ -121,21 +173,6 @@ link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog)
    }
 
 
-   /* Varying variables are treated like other vertex program outputs
-    * (and like other fragment program inputs).  The position of the
-    * first varying differs for vertex/fragment programs...
-    * Also, replace File=PROGRAM_VARYING with File=PROGRAM_INPUT/OUTPUT.
-    */
-   if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
-      firstVarying = VERT_RESULT_VAR0;
-      newFile = PROGRAM_OUTPUT;
-   }
-   else {
-      assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
-      firstVarying = FRAG_ATTRIB_VAR0;
-      newFile = PROGRAM_INPUT;
-   }
-
    /* OK, now scan the program/shader instructions looking for varying vars,
     * replacing the old index with the new index.
     */
@@ -170,13 +207,27 @@ link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog)
  * Build the shProg->Uniforms list.
  * This is basically a list/index of all uniforms found in either/both of
  * the vertex and fragment shaders.
+ *
+ * About uniforms:
+ * Each uniform has two indexes, one that points into the vertex
+ * program's parameter array and another that points into the fragment
+ * program's parameter array.  When the user changes a uniform's value
+ * we have to change the value in the vertex and/or fragment program's
+ * parameter array.
+ *
+ * This function will be called twice to set up the two uniform->parameter
+ * mappings.
+ *
+ * If a uniform is only present in the vertex program OR fragment program
+ * then the fragment/vertex parameter index, respectively, will be -1.
  */
-static void
-link_uniform_vars(struct gl_shader_program *shProg,
+static GLboolean
+link_uniform_vars(GLcontext *ctx,
+                  struct gl_shader_program *shProg,
                   struct gl_program *prog,
                   GLuint *numSamplers)
 {
-   GLuint samplerMap[MAX_SAMPLERS];
+   GLuint samplerMap[200]; /* max number of samplers declared, not used */
    GLuint i;
 
    for (i = 0; i < prog->Parameters->NumParameters; i++) {
@@ -191,27 +242,41 @@ link_uniform_vars(struct gl_shader_program *shProg,
        * Furthermore, we'll need to fix the state-var's size/datatype info.
        */
 
-      if ((p->Type == PROGRAM_UNIFORM && p->Used) ||
-          p->Type == PROGRAM_SAMPLER) {
+      if ((p->Type == PROGRAM_UNIFORM || p->Type == PROGRAM_SAMPLER)
+          && p->Used) {
+         /* add this uniform, indexing into the target's Parameters list */
          struct gl_uniform *uniform =
             _mesa_append_uniform(shProg->Uniforms, p->Name, prog->Target, i);
          if (uniform)
             uniform->Initialized = p->Initialized;
       }
 
-      if (p->Type == PROGRAM_SAMPLER) {
+      /* The samplerMap[] table we build here is used to remap/re-index
+       * sampler references by TEX instructions.
+       */
+      if (p->Type == PROGRAM_SAMPLER && p->Used) {
          /* Allocate a new sampler index */
-         GLuint sampNum = *numSamplers;
          GLuint oldSampNum = (GLuint) prog->Parameters->ParameterValues[i][0];
-         assert(oldSampNum < MAX_SAMPLERS);
-         samplerMap[oldSampNum] = sampNum;
+         GLuint newSampNum = *numSamplers;
+         if (newSampNum >= ctx->Const.MaxTextureImageUnits) {
+            char s[100];
+            sprintf(s, "Too many texture samplers (%u, max is %u)",
+                    newSampNum, ctx->Const.MaxTextureImageUnits);
+            link_error(shProg, s);
+            return GL_FALSE;
+         }
+         /* save old->new mapping in the table */
+         if (oldSampNum < Elements(samplerMap))
+            samplerMap[oldSampNum] = newSampNum;
+         /* update parameter's sampler index */
+         prog->Parameters->ParameterValues[i][0] = (GLfloat) newSampNum;
          (*numSamplers)++;
       }
    }
 
-
-   /* OK, now scan the program/shader instructions looking for sampler vars,
-    * replacing the old index with the new index.
+   /* OK, now scan the program/shader instructions looking for texture
+    * instructions using sampler vars.  Replace old sampler indexes with
+    * new ones.
     */
    prog->SamplersUsed = 0x0;
    for (i = 0; i < prog->NumInstructions; i++) {
@@ -222,13 +287,17 @@ link_uniform_vars(struct gl_shader_program *shProg,
                 inst->Sampler, map[ inst->Sampler ]);
          */
          /* here, texUnit is really samplerUnit */
-         assert(inst->TexSrcUnit < MAX_SAMPLERS);
-         inst->TexSrcUnit = samplerMap[inst->TexSrcUnit];
-         prog->SamplerTargets[inst->TexSrcUnit] = inst->TexSrcTarget;
-         prog->SamplersUsed |= (1 << inst->TexSrcUnit);
+         const GLint oldSampNum = inst->TexSrcUnit;
+         if (oldSampNum < Elements(samplerMap)) {
+            const GLuint newSampNum = samplerMap[oldSampNum];
+            inst->TexSrcUnit = newSampNum;
+            prog->SamplerTargets[newSampNum] = inst->TexSrcTarget;
+            prog->SamplersUsed |= (1 << newSampNum);
+         }
       }
    }
 
+   return GL_TRUE;
 }
 
 
@@ -422,7 +491,6 @@ _slang_update_inputs_outputs(struct gl_program *prog)
          maxAddrReg = MAX2(maxAddrReg, inst->DstReg.Index + 1);
       }
    }
-
    prog->NumAddressRegs = maxAddrReg;
 }
 
@@ -524,10 +592,18 @@ _slang_link(GLcontext *ctx,
    }
 
    /* link uniform vars */
-   if (shProg->VertexProgram)
-      link_uniform_vars(shProg, &shProg->VertexProgram->Base, &numSamplers);
-   if (shProg->FragmentProgram)
-      link_uniform_vars(shProg, &shProg->FragmentProgram->Base, &numSamplers);
+   if (shProg->VertexProgram) {
+      if (!link_uniform_vars(ctx, shProg, &shProg->VertexProgram->Base,
+                             &numSamplers)) {
+         return;
+      }
+   }
+   if (shProg->FragmentProgram) {
+      if (!link_uniform_vars(ctx, shProg, &shProg->FragmentProgram->Base,
+                             &numSamplers)) {
+         return;
+      }
+   }
 
    /*_mesa_print_uniforms(shProg->Uniforms);*/
 
@@ -605,6 +681,11 @@ _slang_link(GLcontext *ctx,
       }
    }
 
+   if (MESA_VERBOSE & VERBOSE_GLSL_DUMP) {
+      printf("Varying vars:\n");
+      _mesa_print_parameter_list(shProg->Varying);
+   }
+
    shProg->LinkStatus = (shProg->VertexProgram || shProg->FragmentProgram);
 }