added _mesa_combine_parameter_lists()
authorBrian Paul <brian.paul@tungstengraphics.com>
Wed, 14 May 2008 18:53:03 +0000 (12:53 -0600)
committerBrian Paul <brian.paul@tungstengraphics.com>
Wed, 14 May 2008 18:53:03 +0000 (12:53 -0600)
cherry-picked from gallium-0.1

src/mesa/shader/prog_parameter.c
src/mesa/shader/prog_parameter.h
src/mesa/shader/program.c
src/mesa/shader/program.h
src/mesa/shader/programopt.c
src/mesa/shader/programopt.h

index 3ad7215755d3dacfd79dde4a1f958fb7b54064a7..500cfbbd8732bbee2a0bca24225b1b82984a2e3f 100644 (file)
@@ -603,6 +603,39 @@ _mesa_clone_parameter_list(const struct gl_program_parameter_list *list)
 }
 
 
+/**
+ * Return a new parameter list which is listA + listB.
+ */
+struct gl_program_parameter_list *
+_mesa_combine_parameter_lists(const struct gl_program_parameter_list *listA,
+                              const struct gl_program_parameter_list *listB)
+{
+   struct gl_program_parameter_list *list;
+
+   if (listA) {
+      list = _mesa_clone_parameter_list(listA);
+      if (list && listB) {
+         GLuint i;
+         for (i = 0; i < listB->NumParameters; i++) {
+            struct gl_program_parameter *param = listB->Parameters + i;
+            _mesa_add_parameter(list, param->Type, param->Name, param->Size,
+                                param->DataType,
+                                listB->ParameterValues[i],
+                                param->StateIndexes);
+         }
+      }
+   }
+   else if (listB) {
+      list = _mesa_clone_parameter_list(listB);
+   }
+   else {
+      list = NULL;
+   }
+   return list;
+}
+
+
+
 /**
  * Find longest name of all uniform parameters in list.
  */
index 09ff851ea73ae90f67d2c54e4db0fb2478f93fa0..105f6f24debe9909d599aef957fac2af55acd2b1 100644 (file)
@@ -78,6 +78,16 @@ _mesa_free_parameter_list(struct gl_program_parameter_list *paramList);
 extern struct gl_program_parameter_list *
 _mesa_clone_parameter_list(const struct gl_program_parameter_list *list);
 
+extern struct gl_program_parameter_list *
+_mesa_combine_parameter_lists(const struct gl_program_parameter_list *a,
+                              const struct gl_program_parameter_list *b);
+
+static INLINE GLuint
+_mesa_num_parameters(const struct gl_program_parameter_list *list)
+{
+   return list ? list->NumParameters : 0;
+}
+
 extern GLint
 _mesa_add_parameter(struct gl_program_parameter_list *paramList,
                     enum register_file type, const char *name,
index 92aa6ee44a38b90d21e6acb428c7d4653d361f3d..14e401e57167cc1e30c6d43da89540042bbee4e4 100644 (file)
@@ -526,6 +526,155 @@ _mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count)
 }
 
 
+/**
+ * Search instructions for registers that match (oldFile, oldIndex),
+ * replacing them with (newFile, newIndex).
+ */
+static void
+replace_registers(struct prog_instruction *inst, GLuint numInst,
+                  GLuint oldFile, GLuint oldIndex,
+                  GLuint newFile, GLuint newIndex)
+{
+   GLuint i, j;
+   for (i = 0; i < numInst; i++) {
+      for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
+         if (inst[i].SrcReg[j].File == oldFile &&
+             inst[i].SrcReg[j].Index == oldIndex) {
+            inst[i].SrcReg[j].File = newFile;
+            inst[i].SrcReg[j].Index = newIndex;
+         }
+      }
+   }
+}
+
+
+/**
+ * Search instructions for references to program parameters.  When found,
+ * increment the parameter index by 'offset'.
+ * Used when combining programs.
+ */
+static void
+adjust_param_indexes(struct prog_instruction *inst, GLuint numInst,
+                     GLuint offset)
+{
+   GLuint i, j;
+   for (i = 0; i < numInst; i++) {
+      for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
+         GLuint f = inst[i].SrcReg[j].File;
+         if (f == PROGRAM_CONSTANT ||
+             f == PROGRAM_UNIFORM ||
+             f == PROGRAM_STATE_VAR) {
+            inst[i].SrcReg[j].Index += offset;
+         }
+      }
+   }
+}
+
+
+/**
+ * Combine two programs into one.  Fix instructions so the outputs of
+ * the first program go to the inputs of the second program.
+ */
+struct gl_program *
+_mesa_combine_programs(GLcontext *ctx,
+                       struct gl_program *progA, struct gl_program *progB)
+{
+   struct prog_instruction *newInst;
+   struct gl_program *newProg;
+   const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */
+   const GLuint lenB = progB->NumInstructions;
+   const GLuint numParamsA = _mesa_num_parameters(progA->Parameters);
+   const GLuint newLength = lenA + lenB;
+   GLuint i;
+
+   ASSERT(progA->Target == progB->Target);
+
+   newInst = _mesa_alloc_instructions(newLength);
+   if (!newInst)
+      return GL_FALSE;
+
+   _mesa_copy_instructions(newInst, progA->Instructions, lenA);
+   _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB);
+
+   /* adjust branch / instruction addresses for B's instructions */
+   for (i = 0; i < lenB; i++) {
+      newInst[lenA + i].BranchTarget += lenA;
+   }
+
+   newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0);
+   newProg->Instructions = newInst;
+   newProg->NumInstructions = newLength;
+
+   if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) {
+      /* connect color outputs/inputs */
+      if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) &&
+          (progB->InputsRead & (1 << FRAG_ATTRIB_COL0))) {
+         replace_registers(newInst + lenA, lenB,
+                           PROGRAM_INPUT, FRAG_ATTRIB_COL0,
+                           PROGRAM_OUTPUT, FRAG_RESULT_COLR);
+      }
+
+      newProg->InputsRead = progA->InputsRead;
+      newProg->InputsRead |= (progB->InputsRead & ~(1 << FRAG_ATTRIB_COL0));
+      newProg->OutputsWritten = progB->OutputsWritten;
+   }
+   else {
+      /* vertex program */
+      assert(0);      /* XXX todo */
+   }
+
+   /*
+    * Merge parameters (uniforms, constants, etc)
+    */
+   newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters,
+                                                       progB->Parameters);
+
+   adjust_param_indexes(newInst + lenA, lenB, numParamsA);
+
+
+   return newProg;
+}
+
+
+
+
+/**
+ * Scan the given program to find a free register of the given type.
+ * \param regFile - PROGRAM_INPUT, PROGRAM_OUTPUT or PROGRAM_TEMPORARY
+ */
+GLint
+_mesa_find_free_register(const struct gl_program *prog, GLuint regFile)
+{
+   GLboolean used[MAX_PROGRAM_TEMPS];
+   GLuint i, k;
+
+   assert(regFile == PROGRAM_INPUT ||
+          regFile == PROGRAM_OUTPUT ||
+          regFile == PROGRAM_TEMPORARY);
+
+   _mesa_memset(used, 0, sizeof(used));
+
+   for (i = 0; i < prog->NumInstructions; i++) {
+      const struct prog_instruction *inst = prog->Instructions + i;
+      const GLuint n = _mesa_num_inst_src_regs(inst->Opcode);
+
+      for (k = 0; k < n; k++) {
+         if (inst->SrcReg[k].File == regFile) {
+            used[inst->SrcReg[k].Index] = GL_TRUE;
+         }
+      }
+   }
+
+   for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
+      if (!used[i])
+         return i;
+   }
+
+   return -1;
+}
+
+
+
 /**
  * Mixing ARB and NV vertex/fragment programs can be tricky.
  * Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV
index c5d36c78a03e5c3d91bcbf8480191a3ca5b5e54a..0f0d6060a9e6eae4b64e00fd3e4a2841b557e625 100644 (file)
@@ -115,6 +115,14 @@ _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog);
 extern  GLboolean
 _mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count);
 
+extern struct gl_program *
+_mesa_combine_programs(GLcontext *ctx,
+                       struct gl_program *progA, struct gl_program *progB);
+
+extern GLint
+_mesa_find_free_register(const struct gl_program *prog, GLuint regFile);
+
+
 /*
  * API functions common to ARB/NV_vertex/fragment_program
  */
index fc5b0497fe369d51eca446e926300e6316e9e637..e6f065a33b884fb60b278ef8eb95bca9991f816b 100644 (file)
@@ -35,6 +35,7 @@
 #include "context.h"
 #include "prog_parameter.h"
 #include "prog_statevars.h"
+#include "program.h"
 #include "programopt.h"
 #include "prog_instruction.h"
 
@@ -102,7 +103,7 @@ _mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
    _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen);
 
    /* free old instructions */
-   _mesa_free(vprog->Base.Instructions);
+   _mesa_free_instructions(vprog->Base.Instructions, origLen);
 
    /* install new instructions */
    vprog->Base.Instructions = newInst;
@@ -275,7 +276,7 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
    inst++;
 
    /* free old instructions */
-   _mesa_free(fprog->Base.Instructions);
+   _mesa_free_instructions(fprog->Base.Instructions, origLen);
 
    /* install new instructions */
    fprog->Base.Instructions = newInst;
@@ -365,3 +366,94 @@ _mesa_count_texture_instructions(struct gl_program *prog)
    }
 }
 
+
+/**
+ * Scan/rewrite program to remove reads of varying (output) registers.
+ * In GLSL vertex shaders, varying vars can be read and written.
+ * Normally, vertex varying vars are implemented as output registers.
+ * On some hardware, trying to read an output register causes trouble.
+ * So, rewrite the program to use a temporary register in this case.
+ */
+void
+_mesa_remove_varying_reads(struct gl_program *prog)
+{
+   GLuint i;
+   GLint outputMap[VERT_RESULT_MAX];
+   GLuint numVaryingReads = 0;
+
+   assert(prog->Target == GL_VERTEX_PROGRAM_ARB);
+
+   for (i = 0; i < VERT_RESULT_MAX; i++)
+      outputMap[i] = -1;
+
+   /* look for instructions which read from varying vars */
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+      GLuint j;
+      for (j = 0; j < numSrc; j++) {
+         if (inst->SrcReg[j].File == PROGRAM_VARYING) {
+            /* replace the read with a temp reg */
+            const GLuint var = inst->SrcReg[j].Index;
+            if (outputMap[var] == -1) {
+               numVaryingReads++;
+               outputMap[var] = _mesa_find_free_register(prog,
+                                                         PROGRAM_TEMPORARY);
+            }
+            inst->SrcReg[j].File = PROGRAM_TEMPORARY;
+            inst->SrcReg[j].Index = outputMap[var];
+         }
+      }
+   }
+
+   if (numVaryingReads == 0)
+      return; /* nothing to be done */
+
+   /* look for instructions which write to the varying vars identified above */
+   for (i = 0; i < prog->NumInstructions; i++) {
+      struct prog_instruction *inst = prog->Instructions + i;
+      const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+      GLuint j;
+      for (j = 0; j < numSrc; j++) {
+         if (inst->DstReg.File == PROGRAM_VARYING &&
+             outputMap[inst->DstReg.Index] >= 0) {
+            /* change inst to write to the temp reg, instead of the varying */
+            inst->DstReg.File = PROGRAM_TEMPORARY;
+            inst->DstReg.Index = outputMap[inst->DstReg.Index];
+         }
+      }
+   }
+
+   /* insert new instructions to copy the temp vars to the varying vars */
+   {
+      struct prog_instruction *inst;
+      GLint endPos, var;
+
+      /* Look for END instruction and insert the new varying writes */
+      endPos = -1;
+      for (i = 0; i < prog->NumInstructions; i++) {
+         struct prog_instruction *inst = prog->Instructions + i;
+         if (inst->Opcode == OPCODE_END) {
+            endPos = i;
+            _mesa_insert_instructions(prog, i, numVaryingReads);
+            break;
+         }
+      }
+
+      assert(endPos >= 0);
+
+      /* insert new MOV instructions here */
+      inst = prog->Instructions + endPos;
+      for (var = 0; var < VERT_RESULT_MAX; var++) {
+         if (outputMap[var] >= 0) {
+            /* MOV VAR[var], TEMP[tmp]; */
+            inst->Opcode = OPCODE_MOV;
+            inst->DstReg.File = PROGRAM_VARYING;
+            inst->DstReg.Index = var;
+            inst->SrcReg[0].File = PROGRAM_TEMPORARY;
+            inst->SrcReg[0].Index = outputMap[var];
+            inst++;
+         }
+      }
+   }
+}
index ce63644bbf5740aa18b266735c8e1138f2df5372..47ff2f0c7be118ed3b18f25120cdb1e5c681e731 100644 (file)
@@ -39,5 +39,7 @@ _mesa_count_texture_indirections(struct gl_program *prog);
 extern void
 _mesa_count_texture_instructions(struct gl_program *prog);
 
+extern void
+_mesa_remove_varying_reads(struct gl_program *prog);
 
 #endif /* PROGRAMOPT_H */