mesa: new _mesa_remove_varying_reads() function
authorBrian <brian.paul@tungstengraphics.com>
Mon, 7 Apr 2008 17:20:21 +0000 (11:20 -0600)
committerBrian <brian.paul@tungstengraphics.com>
Mon, 7 Apr 2008 17:23:44 +0000 (11:23 -0600)
We'll apply this function to GLSL vertex programs.  In GLSL it's legal to
read and write varying (output) vars in a vertex shader.  But reading from
an output register isn't supported by all hardware.  This routine examines
the vertex program for that condition and rewrites it to use temporary
registers where needed.

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

index 9eeb71db1b6e7051db3de4531a4906007bb13cc4..7d560c74a5430d78f03599ee0a62cfb6f000b9ed 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;
@@ -274,7 +275,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;
@@ -364,3 +365,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 */