The new linker actually does a few things right now:
authorBrian <brian@yutani.localnet.net>
Thu, 14 Dec 2006 20:58:32 +0000 (13:58 -0700)
committerBrian <brian@yutani.localnet.net>
Thu, 14 Dec 2006 20:58:32 +0000 (13:58 -0700)
The varying vars used by the vertex and fragment shader are merged so they
agree.
Similarly, uniforms are merged (along with constants, etc).
The vertex/fragment program instructions are then cloned and rewritten
with the newly resolved uniform/varying locations.

src/mesa/shader/slang/slang_link2.c

index e62cc01b3e1bde27cbe720bf51100799a0d8986a..76954e17a645c711ac1dff3a684b8336b468be46 100644 (file)
 #include "hash.h"
 #include "macros.h"
 #include "program.h"
+#include "program_instruction.h"
 #include "shaderobjects.h"
 #include "slang_link.h"
 
 
 
 
-#define RELEASE_GENERIC(x)\
-   (**x)._unknown.Release ((struct gl2_unknown_intf **) (x))
-
-#define RELEASE_CONTAINER(x)\
-   (**x)._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
-
-#define RELEASE_PROGRAM(x)\
-   (**x)._container._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
+static GLboolean
+link_varying_vars(struct gl_linked_program *linked, struct gl_program *prog)
+{
+   GLuint *map, i, firstVarying, newFile;
+   GLbitfield varsWritten, varsRead;
+
+   map = (GLuint *) malloc(prog->Varying->NumParameters * sizeof(GLuint));
+   if (!map)
+      return GL_FALSE;
+
+   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(linked->Varying, -1, var->Name);
+      if (j >= 0) {
+         /* already in list, check size */
+         if (var->Size != linked->Varying->Parameters[j].Size) {
+            /* error */
+            return GL_FALSE;
+         }
+      }
+      else {
+         /* not already in linked list */
+         j = _mesa_add_varying(linked->Varying, var->Name, var->Size);
+      }
+      ASSERT(j >= 0);
 
-#define RELEASE_SHADER(x)\
-   (**x)._generic._unknown.Release ((struct gl2_unknown_intf **) (x))
+      map[i] = j;
+   }
 
 
+   /* 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;
+   }
 
-static struct gl2_unknown_intf **
-lookup_handle(GLcontext * ctx, GLhandleARB handle, enum gl2_uiid uiid,
-              const char *function)
-{
-   struct gl2_unknown_intf **unk;
+   /* keep track of which varying vars we read and write */
+   varsWritten = varsRead = 0x0;
 
-   /*
-    * Note: _mesa_HashLookup() requires non-zero input values, so the
-    * passed-in handle value must be checked beforehand.
+   /* OK, now scan the program/shader instructions looking for varying vars,
+    * replacing the old index with the new index.
     */
-   if (handle == 0) {
-      _mesa_error(ctx, GL_INVALID_VALUE, function);
-      return NULL;
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      GLuint j;
+
+      if (inst->DstReg.File == PROGRAM_VARYING) {
+         inst->DstReg.File = newFile;
+         inst->DstReg.Index = map[ inst->DstReg.Index ] + firstVarying;
+         varsWritten |= (1 << inst->DstReg.Index);
+      }
+
+      for (j = 0; j < 3; j++) {
+         if (inst->SrcReg[j].File == PROGRAM_VARYING) {
+            inst->SrcReg[j].File = newFile;
+            inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ] + firstVarying;
+            varsRead |= (1 << inst->DstReg.Index);
+         }
+      }
+      /* XXX update program OutputsWritten, InputsRead */
    }
-   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
-   unk = (struct gl2_unknown_intf **) _mesa_HashLookup(ctx->Shared->GL2Objects,
-                                                       handle);
-   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
 
-   if (unk == NULL) {
-      _mesa_error(ctx, GL_INVALID_VALUE, function);
+   if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
+      prog->OutputsWritten |= varsWritten;
    }
    else {
-      unk = (**unk).QueryInterface(unk, uiid);
-      if (unk == NULL)
-         _mesa_error(ctx, GL_INVALID_OPERATION, function);
+      assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
+      prog->InputsRead |= varsRead;
    }
-   return unk;
-}
 
-#define GET_GENERIC(x, handle, function)\
-   struct gl2_generic_intf **x = (struct gl2_generic_intf **)\
-                                 lookup_handle (ctx, handle, UIID_GENERIC, function);
 
-#define GET_CONTAINER(x, handle, function)\
-   struct gl2_container_intf **x = (struct gl2_container_intf **)\
-                                   lookup_handle (ctx, handle, UIID_CONTAINER, function);
+   free(map);
+
+   return GL_TRUE;
+}
 
-#define GET_PROGRAM(x, handle, function)\
-   struct gl2_program_intf **x = (struct gl2_program_intf **)\
-                                 lookup_handle (ctx, handle, UIID_PROGRAM, function);
 
-#define GET_SHADER(x, handle, function)\
-   struct gl2_shader_intf **x = (struct gl2_shader_intf **)\
-                                lookup_handle (ctx, handle, UIID_SHADER, function);
+static GLboolean
+is_uniform(enum register_file file)
+{
+   return (file == PROGRAM_ENV_PARAM ||
+           file == PROGRAM_STATE_VAR ||
+           file == PROGRAM_NAMED_PARAM ||
+           file == PROGRAM_CONSTANT ||
+           file == PROGRAM_UNIFORM);
+}
 
 
-static void
-prelink(GLhandleARB programObj, struct gl_linked_program *linked)
+static GLboolean
+link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
 {
-   GET_CURRENT_CONTEXT(ctx);
-
-   linked->VertexProgram = NULL;
-   linked->FragmentProgram = NULL;
-
-   if (programObj != 0) {
-      GET_PROGRAM(program, programObj, "glUseProgramObjectARB(program)");
-
-      if (program == NULL)
-         return;
-
-      /* XXX terrible hack to find the real vertex/fragment programs */
-      {
-         GLuint handle;
-         GLsizei cnt, i;
-         cnt = (**program)._container.GetAttachedCount((struct gl2_container_intf **) (program));
-
-         for (i = 0; i < cnt; i++) {
-            struct gl2_generic_intf **x
-               = (**program)._container.GetAttached((struct gl2_container_intf **) program, i);
-            handle = (**x).GetName(x);
-            {
-               struct gl_program *prog;
-               GET_SHADER(sha, handle, "foo");
-               if (sha && (*sha)->Program) {
-                  prog = (*sha)->Program;
-                  if (prog->Target == GL_VERTEX_PROGRAM_ARB)
-                     linked->VertexProgram = (struct gl_vertex_program *) prog;
-                  else if (prog->Target == GL_FRAGMENT_PROGRAM_ARB)
-                     linked->FragmentProgram = (struct gl_fragment_program *) prog;
-               }
-            }
-#if 0
-            if (linked->VertexProgram)
-               printf("Found vert prog %p %d\n",
-                      linked->VertexProgram,
-                      linked->VertexProgram->Base.NumInstructions);
-            if (linked->FragmentProgram)
-               printf("Found frag prog %p %d\n",
-                      linked->FragmentProgram,
-                      linked->FragmentProgram->Base.NumInstructions);
-#endif
-            RELEASE_GENERIC(x);
+   GLuint *map, i;
+
+   map = (GLuint *) malloc(prog->Parameters->NumParameters * sizeof(GLuint));
+   if (!map)
+      return GL_FALSE;
+
+   for (i = 0; i < prog->Parameters->NumParameters; i++) {
+      /* see if this uniform is in the linked uniform list */
+      const struct gl_program_parameter *p = prog->Parameters->Parameters + i;
+      const GLfloat *pVals = prog->Parameters->ParameterValues[i];
+      GLint j;
+
+      /* sanity check */
+      assert(is_uniform(p->Type));
+
+      if (p->Name) {
+         j = _mesa_lookup_parameter_index(linked->Uniforms, -1, p->Name);
+      }
+      else {
+         GLuint swizzle;
+         ASSERT(p->Type == PROGRAM_CONSTANT);
+         if (_mesa_lookup_parameter_constant(linked->Uniforms, pVals,
+                                             p->Size, &j, &swizzle)) {
+            assert(j >= 0);
+         }
+         else {
+            j = -1;
          }
       }
 
+      if (j >= 0) {
+         /* already in list, check size XXX check this */
+         assert(p->Size == linked->Uniforms->Parameters[j].Size);
+      }
+      else {
+         /* not already in linked list */
+         switch (p->Type) {
+         case PROGRAM_ENV_PARAM:
+            j = _mesa_add_named_parameter(linked->Uniforms, p->Name, pVals);
+         case PROGRAM_CONSTANT:
+            j = _mesa_add_named_constant(linked->Uniforms, p->Name, pVals, p->Size);
+            break;
+         case PROGRAM_STATE_VAR:
+            j = _mesa_add_state_reference(linked->Uniforms, (const GLint *) p->StateIndexes);
+            break;
+         case PROGRAM_UNIFORM:
+            j = _mesa_add_uniform(linked->Uniforms, p->Name, p->Size);
+            break;
+         default:
+            abort();
+         }
+
+      }
+      ASSERT(j >= 0);
+
+      map[i] = j;
+   }
+
+
+   /* OK, now scan the program/shader instructions looking for varying vars,
+    * replacing the old index with the new index.
+    */
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      GLuint j;
+
+      if (is_uniform(inst->DstReg.File)) {
+         inst->DstReg.Index = map[ inst->DstReg.Index ];
+      }
+
+      for (j = 0; j < 3; j++) {
+         if (is_uniform(inst->SrcReg[j].File)) {
+            inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ];
+         }
+      }
+      /* XXX update program OutputsWritten, InputsRead */
    }
+
+   free(map);
+
+   return GL_TRUE;
 }
 
 
+static void
+free_linked_program_data(GLcontext *ctx, struct gl_linked_program *linked)
+{
+   if (linked->VertexProgram) {
+      if (linked->VertexProgram->Base.Parameters == linked->Uniforms) {
+         /* to prevent a double-free in the next call */
+         linked->VertexProgram->Base.Parameters = NULL;
+      }
+      _mesa_delete_program(ctx, &linked->VertexProgram->Base);
+      linked->VertexProgram = NULL;
+   }
+
+   if (linked->FragmentProgram) {
+      if (linked->FragmentProgram->Base.Parameters == linked->Uniforms) {
+         /* to prevent a double-free in the next call */
+         linked->FragmentProgram->Base.Parameters = NULL;
+      }
+      _mesa_delete_program(ctx, &linked->FragmentProgram->Base);
+      linked->FragmentProgram = NULL;
+   }
+
+
+   if (linked->Uniforms) {
+      _mesa_free_parameter_list(linked->Uniforms);
+      linked->Uniforms = NULL;
+   }
+
+   if (linked->Varying) {
+      _mesa_free_parameter_list(linked->Varying);
+      linked->Varying = NULL;
+   }
+}
+
 
+/**
+ * Shader linker.  Currently:
+ *
+ * 1. The last attached vertex shader and fragment shader are linked.
+ * 2. Varying vars in the two shaders are combined so their locations
+ *    agree between the vertex and fragment stages.  They're treated as
+ *    vertex program output attribs and as fragment program input attribs.
+ * 3. Uniform vars (including state references, constants, etc) from the
+ *    vertex and fragment shaders are merged into one group.  Recall that
+ *    GLSL uniforms are shared by all linked shaders.
+ * 4. The vertex and fragment programs are cloned and modified to update
+ *    src/dst register references so they use the new, linked uniform/
+ *    varying storage locations.
+ */
 void
 _slang_link2(GLcontext *ctx,
              GLhandleARB programObj,
@@ -161,66 +282,66 @@ _slang_link2(GLcontext *ctx,
 {
    struct gl_vertex_program *vertProg;
    struct gl_fragment_program *fragProg;
+   GLuint i;
 
-   prelink(programObj, linked);
+   free_linked_program_data(ctx, linked);
 
-   vertProg = linked->VertexProgram;
-   fragProg = linked->FragmentProgram;
-
-   /* free old linked data, if any */
-   if (linked->NumUniforms > 0) {
-      GLuint i;
-      for (i = 0; i < linked->NumUniforms; i++) {
-         _mesa_free((char *) linked->Uniforms[i].Name);
-         linked->Uniforms[i].Name = NULL;
-         linked->Uniforms[i].Value = NULL;
-      }
-      linked->NumUniforms = 0;
-   }
+   linked->Uniforms = _mesa_new_parameter_list();
+   linked->Varying = _mesa_new_parameter_list();
 
-   /*
-    * Find uniforms.
-    * XXX what about dups?
+   /**
+    * Find attached vertex shader, fragment shader
     */
-   if (vertProg) {
-      GLuint i;
-      for (i = 0; i < vertProg->Base.Parameters->NumParameters; i++) {
-         struct gl_program_parameter *p
-            = vertProg->Base.Parameters->Parameters + i;
-         if (p->Name) {
-            struct gl_uniform *u = linked->Uniforms + linked->NumUniforms;
-            u->Name = _mesa_strdup(p->Name);
-            u->Value = &vertProg->Base.Parameters->ParameterValues[i][0];
-            linked->NumUniforms++;
-            assert(linked->NumUniforms < MAX_UNIFORMS);
-         }
-      }
+   vertProg = NULL;
+   fragProg = NULL;
+   for (i = 0; i < linked->NumShaders; i++) {
+      if (linked->Shaders[i]->Target == GL_VERTEX_PROGRAM_ARB)
+         vertProg = (struct gl_vertex_program *) linked->Shaders[i];
+      else if (linked->Shaders[i]->Target == GL_FRAGMENT_PROGRAM_ARB)
+         fragProg = (struct gl_fragment_program *) linked->Shaders[i];
+      else
+         _mesa_problem(ctx, "unexpected shader target in slang_link2()");
    }
-   if (fragProg) {
-      GLuint i;
-      for (i = 0; i < fragProg->Base.Parameters->NumParameters; i++) {
-         struct gl_program_parameter *p
-            = fragProg->Base.Parameters->Parameters + i;
-         if (p->Name) {
-            struct gl_uniform *u = linked->Uniforms + linked->NumUniforms;
-            u->Name = _mesa_strdup(p->Name);
-            u->Value = &fragProg->Base.Parameters->ParameterValues[i][0];
-            linked->NumUniforms++;
-            assert(linked->NumUniforms < MAX_UNIFORMS);
-         }
-      }
+   if (!vertProg || !fragProg) {
+      /* XXX is it legal to have one but not the other?? */
+      /* XXX record error */
+      linked->LinkStatus = GL_FALSE;
+      return;
    }
 
-   /* For varying:
-    * scan both programs for varyings, rewrite programs so they agree
-    * on locations of varyings.
+   /*
+    * Make copies of the vertex/fragment programs now since we'll be
+    * changing src/dst registers after merging the uniforms and varying vars.
     */
+   linked->VertexProgram = (struct gl_vertex_program *)
+      _mesa_clone_program(ctx, &vertProg->Base);
+   linked->FragmentProgram = (struct gl_fragment_program *)
+      _mesa_clone_program(ctx, &fragProg->Base);
+
+#if 1
+   printf("************** orig program\n");
+   _mesa_print_program(&fragProg->Base);
+   _mesa_print_program_parameters(ctx, &fragProg->Base);
+#endif
 
-   /**
-    * Linking should _copy_ the vertex and fragment shader code,
-    * rewriting varying references as we go along...
-    */
+   link_varying_vars(linked, &linked->VertexProgram->Base);
+   link_varying_vars(linked, &linked->FragmentProgram->Base);
+
+   link_uniform_vars(linked, &linked->VertexProgram->Base);
+   link_uniform_vars(linked, &linked->FragmentProgram->Base);
+
+   /* The vertex and fragment programs share a common set of uniforms now */
+   _mesa_free_parameter_list(linked->VertexProgram->Base.Parameters);
+   _mesa_free_parameter_list(linked->FragmentProgram->Base.Parameters);
+   linked->VertexProgram->Base.Parameters = linked->Uniforms;
+   linked->FragmentProgram->Base.Parameters = linked->Uniforms;
+
+#if 1
+   printf("************** linked/cloned\n");
+   _mesa_print_program(&linked->FragmentProgram->Base);
+   _mesa_print_program_parameters(ctx, &linked->FragmentProgram->Base);
+#endif
 
-   linked->LinkStatus = (vertProg && fragProg);
+   linked->LinkStatus = (linked->VertexProgram && linked->FragmentProgram);
 }