Implement projective texture sampling, 3D textures. Disable some debug output.
[mesa.git] / src / mesa / shader / slang / slang_link2.c
index 4827c8c4cdecfb3ea559880eba8d64d0fc7fd18e..3a5bce009951a9e39acf059fe1849a34d483d61f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.6
+ * Version:  6.5.3
  *
  * Copyright (C) 2006  Brian Paul   All Rights Reserved.
  *
@@ -23,9 +23,9 @@
  */
 
 /**
- * \file slang_link.c
- * slang linker
- * \author Michal Krol
+ * \file slang_link2.c
+ * GLSL linker
+ * \author Brian Paul
  */
 
 #include "imports.h"
 #include "prog_instruction.h"
 #include "prog_parameter.h"
 #include "prog_print.h"
-#include "shaderobjects.h"
+#include "shader_api.h"
 #include "slang_link.h"
 
 
 
 
 static GLboolean
-link_varying_vars(struct gl_linked_program *linked, struct gl_program *prog)
+link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog)
 {
    GLuint *map, i, firstVarying, newFile;
    GLbitfield varsWritten, varsRead;
@@ -57,17 +57,17 @@ link_varying_vars(struct gl_linked_program *linked, struct gl_program *prog)
       const struct gl_program_parameter *var
          = prog->Varying->Parameters + i;
 
-      GLint j = _mesa_lookup_parameter_index(linked->Varying, -1, var->Name);
+      GLint j = _mesa_lookup_parameter_index(shProg->Varying, -1, var->Name);
       if (j >= 0) {
          /* already in list, check size */
-         if (var->Size != linked->Varying->Parameters[j].Size) {
+         if (var->Size != shProg->Varying->Parameters[j].Size) {
             /* error */
             return GL_FALSE;
          }
       }
       else {
          /* not already in linked list */
-         j = _mesa_add_varying(linked->Varying, var->Name, var->Size);
+         j = _mesa_add_varying(shProg->Varying, var->Name, var->Size);
       }
       ASSERT(j >= 0);
 
@@ -138,35 +138,42 @@ is_uniform(enum register_file file)
            file == PROGRAM_STATE_VAR ||
            file == PROGRAM_NAMED_PARAM ||
            file == PROGRAM_CONSTANT ||
+           file == PROGRAM_SAMPLER ||
            file == PROGRAM_UNIFORM);
 }
 
 
 static GLboolean
-link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
+link_uniform_vars(struct gl_shader_program *shProg, struct gl_program *prog)
 {
    GLuint *map, i;
 
+#if 0
+   printf("================ pre link uniforms ===============\n");
+   _mesa_print_parameter_list(shProg->Uniforms);
+#endif
+
    map = (GLuint *) malloc(prog->Parameters->NumParameters * sizeof(GLuint));
    if (!map)
       return GL_FALSE;
 
-   for (i = 0; i < prog->Parameters->NumParameters; i++) {
+   for (i = 0; i < prog->Parameters->NumParameters; /* incr below*/) {
       /* 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;
+      GLint size;
 
       /* sanity check */
       assert(is_uniform(p->Type));
 
       if (p->Name) {
-         j = _mesa_lookup_parameter_index(linked->Uniforms, -1, p->Name);
+         j = _mesa_lookup_parameter_index(shProg->Uniforms, -1, p->Name);
       }
       else {
          GLuint swizzle;
          ASSERT(p->Type == PROGRAM_CONSTANT);
-         if (_mesa_lookup_parameter_constant(linked->Uniforms, pVals,
+         if (_mesa_lookup_parameter_constant(shProg->Uniforms, pVals,
                                              p->Size, &j, &swizzle)) {
             assert(j >= 0);
          }
@@ -177,21 +184,27 @@ link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
 
       if (j >= 0) {
          /* already in list, check size XXX check this */
-         assert(p->Size == linked->Uniforms->Parameters[j].Size);
+#if 0
+         assert(p->Size == shProg->Uniforms->Parameters[j].Size);
+#endif
       }
       else {
          /* not already in linked list */
          switch (p->Type) {
          case PROGRAM_ENV_PARAM:
-            j = _mesa_add_named_parameter(linked->Uniforms, p->Name, pVals);
+            j = _mesa_add_named_parameter(shProg->Uniforms, p->Name, pVals);
+            break;
          case PROGRAM_CONSTANT:
-            j = _mesa_add_named_constant(linked->Uniforms, p->Name, pVals, p->Size);
+            j = _mesa_add_named_constant(shProg->Uniforms, p->Name, pVals, p->Size);
             break;
          case PROGRAM_STATE_VAR:
-            j = _mesa_add_state_reference(linked->Uniforms, (const GLint *) p->StateIndexes);
+            j = _mesa_add_state_reference(shProg->Uniforms, (const GLint *) p->StateIndexes);
             break;
          case PROGRAM_UNIFORM:
-            j = _mesa_add_uniform(linked->Uniforms, p->Name, p->Size);
+            j = _mesa_add_uniform(shProg->Uniforms, p->Name, p->Size);
+            break;
+         case PROGRAM_SAMPLER:
+            j = _mesa_add_sampler(shProg->Uniforms, p->Name);
             break;
          default:
             abort();
@@ -200,11 +213,32 @@ link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
       }
       ASSERT(j >= 0);
 
-      map[i] = j;
+      size = p->Size;
+      while (size > 0) {
+         map[i] = j;
+         i++;
+         j++;
+         size -= 4;
+      }
+
    }
 
+#if 0
+   printf("================ post link uniforms ===============\n");
+   _mesa_print_parameter_list(shProg->Uniforms);
+#endif
 
-   /* OK, now scan the program/shader instructions looking for varying vars,
+#if 0
+   {
+      GLuint i;
+      for (i = 0; i < prog->Parameters->NumParameters; i++) {
+         printf("map[%d] = %d\n", i, map[i]);
+      }
+      _mesa_print_parameter_list(shProg->Uniforms);
+   }
+#endif
+
+   /* OK, now scan the program/shader instructions looking for uniform vars,
     * replacing the old index with the new index.
     */
    for (i = 0; i < prog->NumInstructions; i++) {
@@ -220,7 +254,14 @@ link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
             inst->SrcReg[j].Index = map[ inst->SrcReg[j].Index ];
          }
       }
-      /* XXX update program OutputsWritten, InputsRead */
+
+      if (inst->Opcode == OPCODE_TEX ||
+          inst->Opcode == OPCODE_TXB ||
+          inst->Opcode == OPCODE_TXP) {
+         printf("====== remap sampler from %d to %d\n",
+                inst->Sampler, map[ inst->Sampler ]);
+         inst->Sampler = map[ inst->Sampler ];
+      }
    }
 
    free(map);
@@ -229,40 +270,121 @@ link_uniform_vars(struct gl_linked_program *linked, struct gl_program *prog)
 }
 
 
+/**
+ * XXX Temporary
+ */
 static void
-free_linked_program_data(GLcontext *ctx, struct gl_linked_program *linked)
+_slang_resolve_branches(struct gl_program *prog)
 {
-   if (linked->VertexProgram) {
-      if (linked->VertexProgram->Base.Parameters == linked->Uniforms) {
-         /* to prevent a double-free in the next call */
-         linked->VertexProgram->Base.Parameters = NULL;
+   struct target {
+      const char *Name;
+      GLuint Pos;
+   };
+   struct target targets[500];
+   GLuint numTargets = 0;
+   GLuint i, j;
+
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      if (inst->Opcode == OPCODE_NOP && inst->Comment) {
+         targets[numTargets].Name = inst->Comment;
+         targets[numTargets].Pos = i;
+         numTargets++;
       }
-      _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;
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      if (inst->Opcode == OPCODE_BRA) {
+         for (j = 0; j < numTargets; j++) {
+            if (!strcmp(inst->Comment, targets[j].Name)) {
+               inst->BranchTarget = targets[j].Pos;
+               break;
+            }
+         }
+         if (j == numTargets) {
+            abort();
+         }
       }
-      _mesa_delete_program(ctx, &linked->FragmentProgram->Base);
-      linked->FragmentProgram = NULL;
    }
+}
 
 
-   if (linked->Uniforms) {
-      _mesa_free_parameter_list(linked->Uniforms);
-      linked->Uniforms = NULL;
+/**
+ * Scan program for texture instructions, lookup sampler/uniform's value
+ * to determine which texture unit to use.
+ * Also, update the program's TexturesUsed[] array.
+ */
+void
+_slang_resolve_samplers(struct gl_shader_program *shProg,
+                        struct gl_program *prog)
+{
+   GLuint i;
+
+   for (i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++)
+      prog->TexturesUsed[i] = 0;
+
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      if (inst->Opcode == OPCODE_TEX ||
+          inst->Opcode == OPCODE_TXB ||
+          inst->Opcode == OPCODE_TXP) {
+         GLint sampleUnit = (GLint) shProg->Uniforms->ParameterValues[inst->Sampler][0];
+         assert(sampleUnit < MAX_TEXTURE_IMAGE_UNITS);
+         inst->TexSrcUnit = sampleUnit;
+
+         prog->TexturesUsed[inst->TexSrcUnit] |= (1 << inst->TexSrcTarget);
+      }
    }
+}
 
-   if (linked->Varying) {
-      _mesa_free_parameter_list(linked->Varying);
-      linked->Varying = NULL;
+
+/**
+ * Scan program instructions to update the program's InputsRead and
+ * OutputsWritten fields.
+ */
+static void
+_slang_update_inputs_outputs(struct gl_program *prog)
+{
+   GLuint i, j;
+
+   prog->InputsRead = 0x0;
+   prog->OutputsWritten = 0x0;
+
+   for (i = 0; i < prog->NumInstructions; i++) {
+      const struct prog_instruction *inst = prog->Instructions + i;
+      const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+      for (j = 0; j < numSrc; j++) {
+         if (inst->SrcReg[j].File == PROGRAM_INPUT) {
+            prog->InputsRead |= 1 << inst->SrcReg[j].Index;
+         }
+      }
+      if (inst->DstReg.File == PROGRAM_OUTPUT) {
+         prog->OutputsWritten |= 1 << inst->DstReg.Index;
+      }
    }
 }
 
 
+
+/** cast wrapper */
+static struct gl_vertex_program *
+vertex_program(struct gl_program *prog)
+{
+   assert(prog->Target == GL_VERTEX_PROGRAM_ARB);
+   return (struct gl_vertex_program *) prog;
+}
+
+
+/** cast wrapper */
+static struct gl_fragment_program *
+fragment_program(struct gl_program *prog)
+{
+   assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB);
+   return (struct gl_fragment_program *) prog;
+}
+
+
 /**
  * Shader linker.  Currently:
  *
@@ -280,70 +402,95 @@ free_linked_program_data(GLcontext *ctx, struct gl_linked_program *linked)
 void
 _slang_link2(GLcontext *ctx,
              GLhandleARB programObj,
-             struct gl_linked_program *linked)
+             struct gl_shader_program *shProg)
 {
    struct gl_vertex_program *vertProg;
    struct gl_fragment_program *fragProg;
    GLuint i;
 
-   free_linked_program_data(ctx, linked);
+   _mesa_free_shader_program_data(ctx, shProg);
 
-   linked->Uniforms = _mesa_new_parameter_list();
-   linked->Varying = _mesa_new_parameter_list();
+   shProg->Uniforms = _mesa_new_parameter_list();
+   shProg->Varying = _mesa_new_parameter_list();
 
    /**
     * Find attached vertex shader, fragment shader
     */
    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];
+   for (i = 0; i < shProg->NumShaders; i++) {
+      if (shProg->Shaders[i]->Type == GL_VERTEX_SHADER)
+         vertProg = vertex_program(shProg->Shaders[i]->Programs[0]);
+      else if (shProg->Shaders[i]->Type == GL_FRAGMENT_SHADER)
+         fragProg = fragment_program(shProg->Shaders[i]->Programs[0]);
       else
          _mesa_problem(ctx, "unexpected shader target in slang_link2()");
    }
    if (!vertProg || !fragProg) {
       /* XXX is it legal to have one but not the other?? */
       /* XXX record error */
-      linked->LinkStatus = GL_FALSE;
+      shProg->LinkStatus = GL_FALSE;
       return;
    }
 
+   if (!vertProg->Base.Varying || !fragProg->Base.Varying) {
+      /* temporary */
+      _mesa_problem(ctx, "vertex/fragment program lacks varying list!");
+      shProg->LinkStatus = GL_FALSE;
+      return;
+   }  
+
    /*
     * 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
+   shProg->VertexProgram
+      = vertex_program(_mesa_clone_program(ctx, &vertProg->Base));
+   shProg->FragmentProgram
+      = fragment_program(_mesa_clone_program(ctx, &fragProg->Base));
 
-   link_varying_vars(linked, &linked->VertexProgram->Base);
-   link_varying_vars(linked, &linked->FragmentProgram->Base);
+   link_varying_vars(shProg, &shProg->VertexProgram->Base);
+   link_varying_vars(shProg, &shProg->FragmentProgram->Base);
 
-   link_uniform_vars(linked, &linked->VertexProgram->Base);
-   link_uniform_vars(linked, &linked->FragmentProgram->Base);
+   link_uniform_vars(shProg, &shProg->VertexProgram->Base);
+   link_uniform_vars(shProg, &shProg->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;
+   _mesa_free_parameter_list(shProg->VertexProgram->Base.Parameters);
+   _mesa_free_parameter_list(shProg->FragmentProgram->Base.Parameters);
+   shProg->VertexProgram->Base.Parameters = shProg->Uniforms;
+   shProg->FragmentProgram->Base.Parameters = shProg->Uniforms;
+
+   _slang_resolve_branches(&shProg->VertexProgram->Base);
+   _slang_resolve_branches(&shProg->FragmentProgram->Base);
+#if 1
+   _slang_resolve_samplers(shProg, &shProg->VertexProgram->Base);
+   _slang_resolve_samplers(shProg, &shProg->FragmentProgram->Base);
+#endif
+   _slang_update_inputs_outputs(&shProg->VertexProgram->Base);
+   _slang_update_inputs_outputs(&shProg->FragmentProgram->Base);
 
 #if 1
-   printf("************** linked/cloned\n");
-   _mesa_print_program(&linked->FragmentProgram->Base);
-   _mesa_print_program_parameters(ctx, &linked->FragmentProgram->Base);
+   printf("************** original fragment program\n");
+   _mesa_print_program(&fragProg->Base);
+   _mesa_print_program_parameters(ctx, &fragProg->Base);
+#endif
+#if 1
+   printf("************** linked fragment prog\n");
+   _mesa_print_program(&shProg->FragmentProgram->Base);
+   _mesa_print_program_parameters(ctx, &shProg->FragmentProgram->Base);
+#endif
+#if 1
+   printf("************** original vertex program\n");
+   _mesa_print_program(&vertProg->Base);
+   _mesa_print_program_parameters(ctx, &fragProg->Base);
+#endif
+#if 1
+   printf("************** linked vertex prog\n");
+   _mesa_print_program(&shProg->VertexProgram->Base);
+   _mesa_print_program_parameters(ctx, &shProg->VertexProgram->Base);
 #endif
 
-   linked->LinkStatus = (linked->VertexProgram && linked->FragmentProgram);
+   shProg->LinkStatus = (shProg->VertexProgram && shProg->FragmentProgram);
 }