mesa: more complete fix for transform_invarient glitches
[mesa.git] / src / mesa / shader / programopt.c
index fc5b0497fe369d51eca446e926300e6316e9e637..f70c75cec8e3c4a6e74777a8ed436723bae1da36 100644 (file)
  */
 
 
-#include "glheader.h"
-#include "context.h"
+#include "main/glheader.h"
+#include "main/context.h"
 #include "prog_parameter.h"
 #include "prog_statevars.h"
+#include "program.h"
 #include "programopt.h"
 #include "prog_instruction.h"
 
@@ -44,8 +45,8 @@
  * into a vertex program.
  * May be used to implement the position_invariant option.
  */
-void
-_mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
+static void
+_mesa_insert_mvp_dp4_code(GLcontext *ctx, struct gl_vertex_program *vprog)
 {
    struct prog_instruction *newInst;
    const GLuint origLen = vprog->Base.NumInstructions;
@@ -102,7 +103,109 @@ _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;
+   vprog->Base.NumInstructions = newLen;
+   vprog->Base.InputsRead |= VERT_BIT_POS;
+   vprog->Base.OutputsWritten |= (1 << VERT_RESULT_HPOS);
+}
+
+
+static void
+_mesa_insert_mvp_mad_code(GLcontext *ctx, struct gl_vertex_program *vprog)
+{
+   struct prog_instruction *newInst;
+   const GLuint origLen = vprog->Base.NumInstructions;
+   const GLuint newLen = origLen + 4;
+   GLuint hposTemp;
+   GLuint i;
+
+   /*
+    * Setup state references for the modelview/projection matrix.
+    * XXX we should check if these state vars are already declared.
+    */
+   static const gl_state_index mvpState[4][STATE_LENGTH] = {
+      { STATE_MVP_MATRIX, 0, 0, 0, STATE_MATRIX_TRANSPOSE },
+      { STATE_MVP_MATRIX, 0, 1, 1, STATE_MATRIX_TRANSPOSE },
+      { STATE_MVP_MATRIX, 0, 2, 2, STATE_MATRIX_TRANSPOSE },
+      { STATE_MVP_MATRIX, 0, 3, 3, STATE_MATRIX_TRANSPOSE },
+   };
+   GLint mvpRef[4];
+
+   for (i = 0; i < 4; i++) {
+      mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters,
+                                            mvpState[i]);
+   }
+
+   /* Alloc storage for new instructions */
+   newInst = _mesa_alloc_instructions(newLen);
+   if (!newInst) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY,
+                  "glProgramString(inserting position_invariant code)");
+      return;
+   }
+
+   /* TEMP hposTemp; */
+   hposTemp = vprog->Base.NumTemporaries++;
+
+   /*
+    * Generated instructions:
+    *    emit_op2(p, OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]);
+    *    emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp);
+    *    emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp);
+    *    emit_op3(p, OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp);
+    */
+   _mesa_init_instructions(newInst, 4);
+
+   newInst[0].Opcode = OPCODE_MUL;
+   newInst[0].DstReg.File = PROGRAM_TEMPORARY;
+   newInst[0].DstReg.Index = hposTemp;
+   newInst[0].DstReg.WriteMask = WRITEMASK_XYZW;
+   newInst[0].SrcReg[0].File = PROGRAM_INPUT;
+   newInst[0].SrcReg[0].Index = VERT_ATTRIB_POS;
+   newInst[0].SrcReg[0].Swizzle = SWIZZLE_XXXX;
+   newInst[0].SrcReg[1].File = PROGRAM_STATE_VAR;
+   newInst[0].SrcReg[1].Index = mvpRef[0];
+   newInst[0].SrcReg[1].Swizzle = SWIZZLE_NOOP;
+
+   for (i = 1; i <= 2; i++) {
+      newInst[i].Opcode = OPCODE_MAD;
+      newInst[i].DstReg.File = PROGRAM_TEMPORARY;
+      newInst[i].DstReg.Index = hposTemp;
+      newInst[i].DstReg.WriteMask = WRITEMASK_XYZW;
+      newInst[i].SrcReg[0].File = PROGRAM_INPUT;
+      newInst[i].SrcReg[0].Index = VERT_ATTRIB_POS;
+      newInst[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(i,i,i,i);
+      newInst[i].SrcReg[1].File = PROGRAM_STATE_VAR;
+      newInst[i].SrcReg[1].Index = mvpRef[i];
+      newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP;
+      newInst[i].SrcReg[2].File = PROGRAM_TEMPORARY;
+      newInst[i].SrcReg[2].Index = hposTemp;
+      newInst[1].SrcReg[2].Swizzle = SWIZZLE_NOOP;
+   }
+
+   newInst[3].Opcode = OPCODE_MAD;
+   newInst[3].DstReg.File = PROGRAM_OUTPUT;
+   newInst[3].DstReg.Index = VERT_RESULT_HPOS;
+   newInst[3].DstReg.WriteMask = WRITEMASK_XYZW;
+   newInst[3].SrcReg[0].File = PROGRAM_INPUT;
+   newInst[3].SrcReg[0].Index = VERT_ATTRIB_POS;
+   newInst[3].SrcReg[0].Swizzle = SWIZZLE_WWWW;
+   newInst[3].SrcReg[1].File = PROGRAM_STATE_VAR;
+   newInst[3].SrcReg[1].Index = mvpRef[3];
+   newInst[3].SrcReg[1].Swizzle = SWIZZLE_NOOP;
+   newInst[3].SrcReg[2].File = PROGRAM_TEMPORARY;
+   newInst[3].SrcReg[2].Index = hposTemp;
+   newInst[3].SrcReg[2].Swizzle = SWIZZLE_NOOP;
+
+
+   /* Append original instructions after new instructions */
+   _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen);
+
+   /* free old instructions */
+   _mesa_free_instructions(vprog->Base.Instructions, origLen);
 
    /* install new instructions */
    vprog->Base.Instructions = newInst;
@@ -112,6 +215,19 @@ _mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
 }
 
 
+void
+_mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
+{
+   if (ctx->mvp_with_dp4) 
+      _mesa_insert_mvp_dp4_code( ctx, vprog );
+   else
+      _mesa_insert_mvp_mad_code( ctx, vprog );
+}
+      
+
+
+
+
 
 /**
  * Append extra instructions onto the given fragment program to implement
@@ -170,7 +286,7 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
       if (inst->Opcode == OPCODE_END)
          break;
       if (inst->DstReg.File == PROGRAM_OUTPUT &&
-          inst->DstReg.Index == FRAG_RESULT_COLR) {
+          inst->DstReg.Index == FRAG_RESULT_COLOR) {
          /* change the instruction to write to colorTemp w/ clamping */
          inst->DstReg.File = PROGRAM_TEMPORARY;
          inst->DstReg.Index = colorTemp;
@@ -192,13 +308,13 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
       inst->DstReg.WriteMask = WRITEMASK_X;
       inst->SrcReg[0].File = PROGRAM_INPUT;
       inst->SrcReg[0].Index = FRAG_ATTRIB_FOGC;
-      inst->SrcReg[0].Swizzle = SWIZZLE_X;
+      inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
       inst->SrcReg[1].File = PROGRAM_STATE_VAR;
       inst->SrcReg[1].Index = fogPRefOpt;
-      inst->SrcReg[1].Swizzle = SWIZZLE_X;
+      inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
       inst->SrcReg[2].File = PROGRAM_STATE_VAR;
       inst->SrcReg[2].Index = fogPRefOpt;
-      inst->SrcReg[2].Swizzle = SWIZZLE_Y;
+      inst->SrcReg[2].Swizzle = SWIZZLE_YYYY;
       inst->SaturateMode = SATURATE_ZERO_ONE;
       inst++;
    }
@@ -214,10 +330,10 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
       inst->SrcReg[0].File = PROGRAM_STATE_VAR;
       inst->SrcReg[0].Index = fogPRefOpt;
       inst->SrcReg[0].Swizzle
-         = (fprog->FogOption == GL_EXP) ? SWIZZLE_Z : SWIZZLE_W;
+         = (fprog->FogOption == GL_EXP) ? SWIZZLE_ZZZZ : SWIZZLE_WWWW;
       inst->SrcReg[1].File = PROGRAM_INPUT;
       inst->SrcReg[1].Index = FRAG_ATTRIB_FOGC;
-      inst->SrcReg[1].Swizzle = SWIZZLE_X;
+      inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
       inst++;
       if (fprog->FogOption == GL_EXP2) {
          /* MUL fogFactorTemp.x, fogFactorTemp.x, fogFactorTemp.x; */
@@ -227,10 +343,10 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
          inst->DstReg.WriteMask = WRITEMASK_X;
          inst->SrcReg[0].File = PROGRAM_TEMPORARY;
          inst->SrcReg[0].Index = fogFactorTemp;
-         inst->SrcReg[0].Swizzle = SWIZZLE_X;
+         inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
          inst->SrcReg[1].File = PROGRAM_TEMPORARY;
          inst->SrcReg[1].Index = fogFactorTemp;
-         inst->SrcReg[1].Swizzle = SWIZZLE_X;
+         inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
          inst++;
       }
       /* EX2_SAT fogFactorTemp.x, -fogFactorTemp.x; */
@@ -240,20 +356,19 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
       inst->DstReg.WriteMask = WRITEMASK_X;
       inst->SrcReg[0].File = PROGRAM_TEMPORARY;
       inst->SrcReg[0].Index = fogFactorTemp;
-      inst->SrcReg[0].NegateBase = GL_TRUE;
-      inst->SrcReg[0].Swizzle = SWIZZLE_X;
+      inst->SrcReg[0].Negate = NEGATE_XYZW;
+      inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
       inst->SaturateMode = SATURATE_ZERO_ONE;
       inst++;
    }
    /* LRP result.color.xyz, fogFactorTemp.xxxx, colorTemp, fogColorRef; */
    inst->Opcode = OPCODE_LRP;
    inst->DstReg.File = PROGRAM_OUTPUT;
-   inst->DstReg.Index = FRAG_RESULT_COLR;
+   inst->DstReg.Index = FRAG_RESULT_COLOR;
    inst->DstReg.WriteMask = WRITEMASK_XYZ;
    inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    inst->SrcReg[0].Index = fogFactorTemp;
-   inst->SrcReg[0].Swizzle
-      = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X);
+   inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
    inst->SrcReg[1].File = PROGRAM_TEMPORARY;
    inst->SrcReg[1].Index = colorTemp;
    inst->SrcReg[1].Swizzle = SWIZZLE_NOOP;
@@ -264,7 +379,7 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
    /* MOV result.color.w, colorTemp.x;  # copy alpha */
    inst->Opcode = OPCODE_MOV;
    inst->DstReg.File = PROGRAM_OUTPUT;
-   inst->DstReg.Index = FRAG_RESULT_COLR;
+   inst->DstReg.Index = FRAG_RESULT_COLOR;
    inst->DstReg.WriteMask = WRITEMASK_W;
    inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    inst->SrcReg[0].Index = colorTemp;
@@ -275,7 +390,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 +480,96 @@ _mesa_count_texture_instructions(struct gl_program *prog)
    }
 }
 
+
+/**
+ * Scan/rewrite program to remove reads of custom (output) registers.
+ * The passed type has to be either PROGRAM_OUTPUT or PROGRAM_VARYING
+ * (for vertex shaders).
+ * In GLSL shaders, varying vars can be read and written.
+ * 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_output_reads(struct gl_program *prog, gl_register_file type)
+{
+   GLuint i;
+   GLint outputMap[VERT_RESULT_MAX];
+   GLuint numVaryingReads = 0;
+
+   assert(type == PROGRAM_VARYING || type == PROGRAM_OUTPUT);
+   assert(prog->Target == GL_VERTEX_PROGRAM_ARB || type != PROGRAM_VARYING);
+
+   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 == type) {
+            /* 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 == type &&
+             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 = type;
+            inst->DstReg.Index = var;
+            inst->SrcReg[0].File = PROGRAM_TEMPORARY;
+            inst->SrcReg[0].Index = outputMap[var];
+            inst++;
+         }
+      }
+   }
+}