Merge branch '7.8'
[mesa.git] / src / mesa / shader / slang / slang_link.c
index 144c1265251f284d2b6fb116a5d28db67c09fc4b..f71fde1d7271741081bd367f27e0caf0c55513b8 100644 (file)
@@ -31,7 +31,6 @@
 
 #include "main/imports.h"
 #include "main/context.h"
-#include "main/hash.h"
 #include "main/macros.h"
 #include "shader/program.h"
 #include "shader/prog_instruction.h"
@@ -69,7 +68,7 @@ static void
 link_error(struct gl_shader_program *shProg, const char *msg)
 {
    if (shProg->InfoLog) {
-      _mesa_free(shProg->InfoLog);
+      free(shProg->InfoLog);
    }
    shProg->InfoLog = _mesa_strdup(msg);
    shProg->LinkStatus = GL_FALSE;
@@ -87,6 +86,107 @@ bits_agree(GLbitfield flags1, GLbitfield flags2, GLbitfield bit)
 }
 
 
+/**
+ * Examine the outputs/varyings written by the vertex shader and
+ * append the names of those outputs onto the Varyings list.
+ * This will only capture the pre-defined/built-in varyings like
+ * gl_Position, not user-defined varyings.
+ */
+static void
+update_varying_var_list(GLcontext *ctx, struct gl_shader_program *shProg)
+{
+   if (shProg->VertexProgram) {
+      GLbitfield64 written = shProg->VertexProgram->Base.OutputsWritten;
+      GLuint i;
+      for (i = 0; written && i < VERT_RESULT_MAX; i++) {
+         if (written & BITFIELD64_BIT(i)) {
+            const char *name = _slang_vertex_output_name(i);            
+            if (name)
+               _mesa_add_varying(shProg->Varying, name, 1, GL_FLOAT_VEC4, 0x0);
+            written &= ~BITFIELD64_BIT(i);
+         }
+      }
+   }
+}
+
+
+/**
+ * Do link error checking related to transform feedback.
+ */
+static GLboolean
+link_transform_feedback(GLcontext *ctx, struct gl_shader_program *shProg)
+{
+   GLbitfield varyingMask;
+   GLuint totalComps, maxComps, i;
+
+   if (shProg->TransformFeedback.NumVarying == 0) {
+      /* nothing to do */
+      return GL_TRUE;
+   }
+
+   /* Check that there's a vertex shader */
+   if (shProg->TransformFeedback.NumVarying > 0 &&
+       !shProg->VertexProgram) {
+      link_error(shProg, "Transform feedback without vertex shader");
+      return GL_FALSE;
+   }
+
+   /* Check that all named variables exist, and that none are duplicated.
+    * Also, build a count of the number of varying components to feedback.
+    */
+   totalComps = 0;
+   varyingMask = 0x0;
+   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
+      const GLchar *name = shProg->TransformFeedback.VaryingNames[i];
+      GLint v = _mesa_lookup_parameter_index(shProg->Varying, -1, name);
+      struct gl_program_parameter *p;
+
+      if (v < 0) {
+         char msg[100];
+         _mesa_snprintf(msg, sizeof(msg),
+                        "vertex shader does not emit %s", name);
+         link_error(shProg, msg);
+         return GL_FALSE;
+      }
+
+      assert(v < MAX_VARYING);
+
+      /* already seen this varying name? */
+      if (varyingMask & (1 << v)) {
+         char msg[100];
+         _mesa_snprintf(msg, sizeof(msg),
+                        "duplicated transform feedback varying name: %s",
+                        name);
+         link_error(shProg, msg);
+         return GL_FALSE;
+      }
+
+      varyingMask |= (1 << v);
+
+      p = &shProg->Varying->Parameters[v];
+      
+      totalComps += _mesa_sizeof_glsl_type(p->DataType);
+   }
+
+   if (shProg->TransformFeedback.BufferMode == GL_INTERLEAVED_ATTRIBS)
+      maxComps = ctx->Const.MaxTransformFeedbackInterleavedComponents;
+   else
+      maxComps = ctx->Const.MaxTransformFeedbackSeparateComponents;
+
+   /* check max varying components against the limit */
+   if (totalComps > maxComps) {
+      char msg[100];
+      _mesa_snprintf(msg, sizeof(msg),
+                     "Too many feedback components: %u, max is %u",
+                     totalComps, maxComps);
+      link_error(shProg, msg);
+      return GL_FALSE;
+   }
+
+   return GL_TRUE;
+}
+
+
 /**
  * Linking varying vars involves rearranging varying vars so that the
  * vertex program's output varyings matches the order of the fragment
@@ -104,7 +204,7 @@ link_varying_vars(GLcontext *ctx,
    GLuint *map, i, firstVarying, newFile;
    GLbitfield *inOutFlags;
 
-   map = (GLuint *) _mesa_malloc(prog->Varying->NumParameters * sizeof(GLuint));
+   map = (GLuint *) malloc(prog->Varying->NumParameters * sizeof(GLuint));
    if (!map)
       return GL_FALSE;
 
@@ -135,35 +235,35 @@ link_varying_vars(GLcontext *ctx,
             &shProg->Varying->Parameters[j];
          if (var->Size != v->Size) {
             link_error(shProg, "mismatched varying variable types");
-            _mesa_free(map);
+            free(map);
             return GL_FALSE;
          }
          if (!bits_agree(var->Flags, v->Flags, PROG_PARAM_BIT_CENTROID)) {
             char msg[100];
             _mesa_snprintf(msg, sizeof(msg),
-                           "centroid modifier mismatch for '%s'", var->Name);
+                    "centroid modifier mismatch for '%s'", var->Name);
             link_error(shProg, msg);
-            _mesa_free(map);
+            free(map);
             return GL_FALSE;
          }
          if (!bits_agree(var->Flags, v->Flags, PROG_PARAM_BIT_INVARIANT)) {
             char msg[100];
             _mesa_snprintf(msg, sizeof(msg),
-                           "invariant modifier mismatch for '%s'", var->Name);
+                    "invariant modifier mismatch for '%s'", var->Name);
             link_error(shProg, msg);
-            _mesa_free(map);
+            free(map);
             return GL_FALSE;
          }
       }
       else {
          /* not already in linked list */
          j = _mesa_add_varying(shProg->Varying, var->Name, var->Size,
-                               var->Flags);
+                               var->DataType, var->Flags);
       }
 
       if (shProg->Varying->NumParameters > ctx->Const.MaxVarying) {
          link_error(shProg, "Too many varying variables");
-         _mesa_free(map);
+         free(map);
          return GL_FALSE;
       }
 
@@ -203,7 +303,7 @@ link_varying_vars(GLcontext *ctx,
       }
    }
 
-   _mesa_free(map);
+   free(map);
 
    /* these will get recomputed before linking is completed */
    prog->InputsRead = 0x0;
@@ -270,8 +370,8 @@ link_uniform_vars(GLcontext *ctx,
          GLuint newSampNum = *numSamplers;
          if (newSampNum >= ctx->Const.MaxTextureImageUnits) {
             char s[100];
-            _mesa_sprintf(s, "Too many texture samplers (%u, max is %u)",
-                          newSampNum, ctx->Const.MaxTextureImageUnits);
+            sprintf(s, "Too many texture samplers (%u, max is %u)",
+                   newSampNum, ctx->Const.MaxTextureImageUnits);
             link_error(shProg, s);
             return GL_FALSE;
          }
@@ -515,7 +615,7 @@ _slang_update_inputs_outputs(struct gl_program *prog)
       }
 
       if (inst->DstReg.File == PROGRAM_OUTPUT) {
-         prog->OutputsWritten |= 1 << inst->DstReg.Index;
+         prog->OutputsWritten |= BITFIELD64_BIT(inst->DstReg.Index);
          if (inst->DstReg.RelAddr) {
             /* If the output attribute is indexed with relative addressing
              * we know that it must be a varying or texcoord such as
@@ -528,14 +628,17 @@ _slang_update_inputs_outputs(struct gl_program *prog)
             if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
                if (inst->DstReg.Index == VERT_RESULT_TEX0) {
                   /* mark all texcoord outputs as written */
-                  const GLbitfield mask =
-                     ((1 << MAX_TEXTURE_COORD_UNITS) - 1) << VERT_RESULT_TEX0;
+                  const GLbitfield64 mask =
+                    BITFIELD64_RANGE(VERT_RESULT_TEX0,
+                                     (VERT_RESULT_TEX0
+                                      + MAX_TEXTURE_COORD_UNITS - 1));
                   prog->OutputsWritten |= mask;
                }
                else if (inst->DstReg.Index == VERT_RESULT_VAR0) {
                   /* mark all generic varying outputs as written */
-                  const GLbitfield mask =
-                     ((1 << MAX_VARYING) - 1) << VERT_RESULT_VAR0;
+                  const GLbitfield64 mask =
+                    BITFIELD64_RANGE(VERT_RESULT_VAR0,
+                                     (VERT_RESULT_VAR0 + MAX_VARYING - 1));
                   prog->OutputsWritten |= mask;
                }
             }
@@ -561,7 +664,7 @@ remove_extra_version_directives(GLchar *source)
 {
    GLuint verCount = 0;
    while (1) {
-      char *ver = _mesa_strstr(source, "#version");
+      char *ver = strstr(source, "#version");
       if (ver) {
          verCount++;
          if (verCount > 1) {
@@ -587,45 +690,61 @@ concat_shaders(struct gl_shader_program *shProg, GLenum shaderType)
 {
    struct gl_shader *newShader;
    const struct gl_shader *firstShader = NULL;
-   GLuint shaderLengths[100];
+   GLuint *shaderLengths;
    GLchar *source;
    GLuint totalLen = 0, len = 0;
    GLuint i;
 
+   shaderLengths = (GLuint *)malloc(shProg->NumShaders * sizeof(GLuint));
+   if (!shaderLengths) {
+      return NULL;
+   }
+
    /* compute total size of new shader source code */
    for (i = 0; i < shProg->NumShaders; i++) {
       const struct gl_shader *shader = shProg->Shaders[i];
       if (shader->Type == shaderType) {
-         shaderLengths[i] = _mesa_strlen(shader->Source);
+         shaderLengths[i] = strlen(shader->Source);
          totalLen += shaderLengths[i];
          if (!firstShader)
             firstShader = shader;
       }
    }
 
-   if (totalLen == 0)
+   if (totalLen == 0) {
+      free(shaderLengths);
       return NULL;
+   }
 
-   source = (GLchar *) _mesa_malloc(totalLen + 1);
-   if (!source)
+   source = (GLchar *) malloc(totalLen + 1);
+   if (!source) {
+      free(shaderLengths);
       return NULL;
+   }
 
    /* concatenate shaders */
    for (i = 0; i < shProg->NumShaders; i++) {
       const struct gl_shader *shader = shProg->Shaders[i];
       if (shader->Type == shaderType) {
-         _mesa_memcpy(source + len, shader->Source, shaderLengths[i]);
+         memcpy(source + len, shader->Source, shaderLengths[i]);
          len += shaderLengths[i];
       }
    }
    source[len] = '\0';
    /*
-   _mesa_printf("---NEW CONCATENATED SHADER---:\n%s\n------------\n", source);
+   printf("---NEW CONCATENATED SHADER---:\n%s\n------------\n", source);
    */
 
+   free(shaderLengths);
+
    remove_extra_version_directives(source);
 
    newShader = CALLOC_STRUCT(gl_shader);
+   if (!newShader) {
+      free(source);
+      return NULL;
+   }
+
    newShader->Type = shaderType;
    newShader->Source = source;
    newShader->Pragmas = firstShader->Pragmas;
@@ -701,6 +820,7 @@ _slang_link(GLcontext *ctx,
 {
    const struct gl_vertex_program *vertProg = NULL;
    const struct gl_fragment_program *fragProg = NULL;
+   GLboolean vertNotify = GL_TRUE, fragNotify = GL_TRUE;
    GLuint numSamplers = 0;
    GLuint i;
 
@@ -754,7 +874,7 @@ _slang_link(GLcontext *ctx,
    _mesa_reference_vertprog(ctx, &shProg->VertexProgram, NULL);
    if (vertProg) {
       struct gl_vertex_program *linked_vprog =
-         vertex_program(_mesa_clone_program(ctx, &vertProg->Base));
+         _mesa_clone_vertex_program(ctx, vertProg);
       shProg->VertexProgram = linked_vprog; /* refcount OK */
       /* vertex program ID not significant; just set Id for debugging purposes */
       shProg->VertexProgram->Base.Id = shProg->Name;
@@ -764,7 +884,7 @@ _slang_link(GLcontext *ctx,
    _mesa_reference_fragprog(ctx, &shProg->FragmentProgram, NULL);
    if (fragProg) {
       struct gl_fragment_program *linked_fprog = 
-         fragment_program(_mesa_clone_program(ctx, &fragProg->Base));
+         _mesa_clone_fragment_program(ctx, fragProg);
       shProg->FragmentProgram = linked_fprog; /* refcount OK */
       /* vertex program ID not significant; just set Id for debugging purposes */
       shProg->FragmentProgram->Base.Id = shProg->Name;
@@ -807,7 +927,8 @@ _slang_link(GLcontext *ctx,
    if (shProg->VertexProgram) {
       _slang_update_inputs_outputs(&shProg->VertexProgram->Base);
       _slang_count_temporaries(&shProg->VertexProgram->Base);
-      if (!(shProg->VertexProgram->Base.OutputsWritten & (1 << VERT_RESULT_HPOS))) {
+      if (!(shProg->VertexProgram->Base.OutputsWritten
+           & BITFIELD64_BIT(VERT_RESULT_HPOS))) {
          /* the vertex program did not compute a vertex position */
          link_error(shProg,
                     "gl_Position was not written by vertex shader\n");
@@ -825,7 +946,7 @@ _slang_link(GLcontext *ctx,
    if (shProg->FragmentProgram) {
       const GLbitfield varyingRead
          = shProg->FragmentProgram->Base.InputsRead >> FRAG_ATTRIB_VAR0;
-      const GLbitfield varyingWritten = shProg->VertexProgram ?
+      const GLbitfield64 varyingWritten = shProg->VertexProgram ?
          shProg->VertexProgram->Base.OutputsWritten >> VERT_RESULT_VAR0 : 0x0;
       if ((varyingRead & varyingWritten) != varyingRead) {
          link_error(shProg,
@@ -836,29 +957,36 @@ _slang_link(GLcontext *ctx,
 
    /* check that gl_FragColor and gl_FragData are not both written to */
    if (shProg->FragmentProgram) {
-      GLbitfield outputsWritten = shProg->FragmentProgram->Base.OutputsWritten;
-      if ((outputsWritten & ((1 << FRAG_RESULT_COLOR))) &&
-          (outputsWritten >= (1 << FRAG_RESULT_DATA0))) {
+      const GLbitfield64 outputsWritten =
+        shProg->FragmentProgram->Base.OutputsWritten;
+      if ((outputsWritten & BITFIELD64_BIT(FRAG_RESULT_COLOR)) &&
+          (outputsWritten >= BITFIELD64_BIT(FRAG_RESULT_DATA0))) {
          link_error(shProg, "Fragment program cannot write both gl_FragColor"
                     " and gl_FragData[].\n");
          return;
       }         
    }
 
+   update_varying_var_list(ctx, shProg);
+
+   /* checks related to transform feedback */
+   if (!link_transform_feedback(ctx, shProg)) {
+      return;
+   }
 
    if (fragProg && shProg->FragmentProgram) {
       /* Compute initial program's TexturesUsed info */
       _mesa_update_shader_textures_used(&shProg->FragmentProgram->Base);
 
       /* notify driver that a new fragment program has been compiled/linked */
-      ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB,
-                                      &shProg->FragmentProgram->Base);
+      vertNotify = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB,
+                                                 &shProg->FragmentProgram->Base);
       if (ctx->Shader.Flags & GLSL_DUMP) {
-         _mesa_printf("Mesa pre-link fragment program:\n");
+         printf("Mesa pre-link fragment program:\n");
          _mesa_print_program(&fragProg->Base);
          _mesa_print_program_parameters(ctx, &fragProg->Base);
 
-         _mesa_printf("Mesa post-link fragment program:\n");
+         printf("Mesa post-link fragment program:\n");
          _mesa_print_program(&shProg->FragmentProgram->Base);
          _mesa_print_program_parameters(ctx, &shProg->FragmentProgram->Base);
       }
@@ -869,14 +997,14 @@ _slang_link(GLcontext *ctx,
       _mesa_update_shader_textures_used(&shProg->VertexProgram->Base);
 
       /* notify driver that a new vertex program has been compiled/linked */
-      ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB,
-                                      &shProg->VertexProgram->Base);
+      fragNotify = ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB,
+                                                   &shProg->VertexProgram->Base);
       if (ctx->Shader.Flags & GLSL_DUMP) {
-         _mesa_printf("Mesa pre-link vertex program:\n");
+         printf("Mesa pre-link vertex program:\n");
          _mesa_print_program(&vertProg->Base);
          _mesa_print_program_parameters(ctx, &vertProg->Base);
 
-         _mesa_printf("Mesa post-link vertex program:\n");
+         printf("Mesa post-link vertex program:\n");
          _mesa_print_program(&shProg->VertexProgram->Base);
          _mesa_print_program_parameters(ctx, &shProg->VertexProgram->Base);
       }
@@ -891,13 +1019,19 @@ _slang_link(GLcontext *ctx,
    }
 
    if (ctx->Shader.Flags & GLSL_DUMP) {
-      _mesa_printf("Varying vars:\n");
+      printf("Varying vars:\n");
       _mesa_print_parameter_list(shProg->Varying);
       if (shProg->InfoLog) {
-         _mesa_printf("Info Log: %s\n", shProg->InfoLog);
+         printf("Info Log: %s\n", shProg->InfoLog);
       }
    }
 
-   shProg->LinkStatus = (shProg->VertexProgram || shProg->FragmentProgram);
+   if (!vertNotify || !fragNotify) {
+      /* driver rejected one/both of the vertex/fragment programs */
+      link_error(shProg, "Vertex and/or fragment program rejected by driver\n");
+   }
+   else {
+      shProg->LinkStatus = (shProg->VertexProgram || shProg->FragmentProgram);
+   }
 }