mesa: also pass the GPU program to _mesa_append_uniforms_to_file()
[mesa.git] / src / mesa / shader / prog_print.c
index 7049c3c9d06e6307a1fe1ca37a83a6aafc2d3f34..3c1c17e0996866021fc3ce170eb8aa599a7fa580 100644 (file)
@@ -3,6 +3,7 @@
  * Version:  7.3
  *
  * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
+ * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -42,7 +43,7 @@
  * Return string name for given program/register file.
  */
 static const char *
-file_string(enum register_file f, gl_prog_print_mode mode)
+file_string(gl_register_file f, gl_prog_print_mode mode)
 {
    switch (f) {
    case PROGRAM_TEMPORARY:
@@ -71,8 +72,14 @@ file_string(enum register_file f, gl_prog_print_mode mode)
       return "ADDR";
    case PROGRAM_SAMPLER:
       return "SAMPLER";
+   case PROGRAM_UNDEFINED:
+      return "UNDEFINED";
    default:
-      return "Unknown program file!";
+      {
+         static char s[20];
+         _mesa_snprintf(s, sizeof(s), "FILE%u", f);
+         return s;
+      }
    }
 }
 
@@ -83,6 +90,9 @@ file_string(enum register_file f, gl_prog_print_mode mode)
 static const char *
 arb_input_attrib_string(GLint index, GLenum progType)
 {
+   /*
+    * These strings should match the VERT_ATTRIB_x and FRAG_ATTRIB_x tokens.
+    */
    const char *vertAttribs[] = {
       "vertex.position",
       "vertex.weight",
@@ -157,6 +167,9 @@ arb_input_attrib_string(GLint index, GLenum progType)
 static const char *
 arb_output_attrib_string(GLint index, GLenum progType)
 {
+   /*
+    * These strings should match the VERT_RESULT_x and FRAG_RESULT_x tokens.
+    */
    const char *vertResults[] = {
       "result.position",
       "result.color.primary",
@@ -181,7 +194,12 @@ arb_output_attrib_string(GLint index, GLenum progType)
    };
    const char *fragResults[] = {
       "result.color",
-      "result.depth"
+      "result.color(half)",
+      "result.depth",
+      "result.color[0]",
+      "result.color[1]",
+      "result.color[2]",
+      "result.color[3]"
    };
 
    if (progType == GL_VERTEX_PROGRAM_ARB) {
@@ -199,64 +217,62 @@ arb_output_attrib_string(GLint index, GLenum progType)
  * Return string representation of the given register.
  * Note that some types of registers (like PROGRAM_UNIFORM) aren't defined
  * by the ARB/NV program languages so we've taken some liberties here.
- * \param file  the register file (PROGRAM_INPUT, PROGRAM_TEMPORARY, etc)
+ * \param f  the register file (PROGRAM_INPUT, PROGRAM_TEMPORARY, etc)
  * \param index  number of the register in the register file
  * \param mode  the output format/mode/style
  * \param prog  pointer to containing program
  */
 static const char *
-reg_string(enum register_file f, GLint index, gl_prog_print_mode mode,
+reg_string(gl_register_file f, GLint index, gl_prog_print_mode mode,
            GLboolean relAddr, const struct gl_program *prog)
 {
    static char str[100];
+   const char *addr = relAddr ? "ADDR+" : "";
 
    str[0] = 0;
 
    switch (mode) {
    case PROG_PRINT_DEBUG:
-      if (relAddr)
-         sprintf(str, "%s[ADDR+%d]", file_string(f, mode), index);
-      else
-         sprintf(str, "%s[%d]", file_string(f, mode), index);
+      _mesa_sprintf(str, "%s[%s%d]", file_string(f, mode), addr, index);
       break;
 
    case PROG_PRINT_ARB:
       switch (f) {
       case PROGRAM_INPUT:
-         sprintf(str, "%s", arb_input_attrib_string(index, prog->Target));
+         _mesa_sprintf(str, "%s", arb_input_attrib_string(index, prog->Target));
          break;
       case PROGRAM_OUTPUT:
-         sprintf(str, "%s", arb_output_attrib_string(index, prog->Target));
+         _mesa_sprintf(str, "%s", arb_output_attrib_string(index, prog->Target));
          break;
       case PROGRAM_TEMPORARY:
-         sprintf(str, "temp%d", index);
+         _mesa_sprintf(str, "temp%d", index);
          break;
       case PROGRAM_ENV_PARAM:
-         sprintf(str, "program.env[%d]", index);
+         _mesa_sprintf(str, "program.env[%s%d]", addr, index);
          break;
       case PROGRAM_LOCAL_PARAM:
-         sprintf(str, "program.local[%d]", index);
+         _mesa_sprintf(str, "program.local[%s%d]", addr, index);
          break;
       case PROGRAM_VARYING: /* extension */
-         sprintf(str, "varying[%d]", index);
+         _mesa_sprintf(str, "varying[%s%d]", addr, index);
          break;
       case PROGRAM_CONSTANT: /* extension */
-         sprintf(str, "constant[%d]", index);
+         _mesa_sprintf(str, "constant[%s%d]", addr, index);
          break;
       case PROGRAM_UNIFORM: /* extension */
-         sprintf(str, "uniform[%d]", index);
+         _mesa_sprintf(str, "uniform[%s%d]", addr, index);
          break;
       case PROGRAM_STATE_VAR:
          {
             struct gl_program_parameter *param
                = prog->Parameters->Parameters + index;
             char *state = _mesa_program_state_string(param->StateIndexes);
-            sprintf(str, state);
+            _mesa_sprintf(str, state);
             _mesa_free(state);
          }
          break;
       case PROGRAM_ADDRESS:
-         sprintf(str, "A%d", index);
+         _mesa_sprintf(str, "A%d", index);
          break;
       default:
          _mesa_problem(NULL, "bad file in reg_string()");
@@ -267,30 +283,30 @@ reg_string(enum register_file f, GLint index, gl_prog_print_mode mode,
       switch (f) {
       case PROGRAM_INPUT:
          if (prog->Target == GL_VERTEX_PROGRAM_ARB)
-            sprintf(str, "v[%d]", index);
+            _mesa_sprintf(str, "v[%d]", index);
          else
-            sprintf(str, "f[%d]", index);
+            _mesa_sprintf(str, "f[%d]", index);
          break;
       case PROGRAM_OUTPUT:
-         sprintf(str, "o[%d]", index);
+         _mesa_sprintf(str, "o[%d]", index);
          break;
       case PROGRAM_TEMPORARY:
-         sprintf(str, "R%d", index);
+         _mesa_sprintf(str, "R%d", index);
          break;
       case PROGRAM_ENV_PARAM:
-         sprintf(str, "c[%d]", index);
+         _mesa_sprintf(str, "c[%d]", index);
          break;
       case PROGRAM_VARYING: /* extension */
-         sprintf(str, "varying[%d]", index);
+         _mesa_sprintf(str, "varying[%s%d]", addr, index);
          break;
       case PROGRAM_UNIFORM: /* extension */
-         sprintf(str, "uniform[%d]", index);
+         _mesa_sprintf(str, "uniform[%s%d]", addr, index);
          break;
       case PROGRAM_CONSTANT: /* extension */
-         sprintf(str, "constant[%d]", index);
+         _mesa_sprintf(str, "constant[%s%d]", addr, index);
          break;
       case PROGRAM_STATE_VAR: /* extension */
-         sprintf(str, "state[%d]", index);
+         _mesa_sprintf(str, "state[%s%d]", addr, index);
          break;
       default:
          _mesa_problem(NULL, "bad file in reg_string()");
@@ -313,19 +329,19 @@ reg_string(enum register_file f, GLint index, gl_prog_print_mode mode,
  * \param extended  if true, also allow 0, 1 values
  */
 const char *
-_mesa_swizzle_string(GLuint swizzle, GLuint negateBase, GLboolean extended)
+_mesa_swizzle_string(GLuint swizzle, GLuint negateMask, GLboolean extended)
 {
    static const char swz[] = "xyzw01!?";  /* See SWIZZLE_x definitions */
    static char s[20];
    GLuint i = 0;
 
-   if (!extended && swizzle == SWIZZLE_NOOP && negateBase == 0)
+   if (!extended && swizzle == SWIZZLE_NOOP && negateMask == 0)
       return ""; /* no swizzle/negation */
 
    if (!extended)
       s[i++] = '.';
 
-   if (negateBase & NEGATE_X)
+   if (negateMask & NEGATE_X)
       s[i++] = '-';
    s[i++] = swz[GET_SWZ(swizzle, 0)];
 
@@ -333,7 +349,7 @@ _mesa_swizzle_string(GLuint swizzle, GLuint negateBase, GLboolean extended)
       s[i++] = ',';
    }
 
-   if (negateBase & NEGATE_Y)
+   if (negateMask & NEGATE_Y)
       s[i++] = '-';
    s[i++] = swz[GET_SWZ(swizzle, 1)];
 
@@ -341,7 +357,7 @@ _mesa_swizzle_string(GLuint swizzle, GLuint negateBase, GLboolean extended)
       s[i++] = ',';
    }
 
-   if (negateBase & NEGATE_Z)
+   if (negateMask & NEGATE_Z)
       s[i++] = '-';
    s[i++] = swz[GET_SWZ(swizzle, 2)];
 
@@ -349,7 +365,7 @@ _mesa_swizzle_string(GLuint swizzle, GLuint negateBase, GLboolean extended)
       s[i++] = ',';
    }
 
-   if (negateBase & NEGATE_W)
+   if (negateMask & NEGATE_W)
       s[i++] = '-';
    s[i++] = swz[GET_SWZ(swizzle, 3)];
 
@@ -414,90 +430,102 @@ _mesa_condcode_string(GLuint condcode)
 
 
 static void
-print_dst_reg(const struct prog_dst_register *dstReg, gl_prog_print_mode mode,
-              const struct gl_program *prog)
+fprint_dst_reg(FILE * f,
+               const struct prog_dst_register *dstReg,
+               gl_prog_print_mode mode,
+               const struct gl_program *prog)
 {
-   _mesa_printf("%s%s",
-                reg_string((enum register_file) dstReg->File,
-                           dstReg->Index, mode, dstReg->RelAddr, prog),
-                _mesa_writemask_string(dstReg->WriteMask));
+   _mesa_fprintf(f, "%s%s",
+                 reg_string((gl_register_file) dstReg->File,
+                            dstReg->Index, mode, dstReg->RelAddr, prog),
+                 _mesa_writemask_string(dstReg->WriteMask));
 
    if (dstReg->CondMask != COND_TR) {
-      _mesa_printf(" (%s.%s)",
-                   _mesa_condcode_string(dstReg->CondMask),
-                   _mesa_swizzle_string(dstReg->CondSwizzle, GL_FALSE, GL_FALSE));
+      _mesa_fprintf(f, " (%s.%s)",
+                    _mesa_condcode_string(dstReg->CondMask),
+                    _mesa_swizzle_string(dstReg->CondSwizzle,
+                                         GL_FALSE, GL_FALSE));
    }
 
 #if 0
-   _mesa_printf("%s[%d]%s",
-                file_string((enum register_file) dstReg->File, mode),
+   _mesa_fprintf(f, "%s[%d]%s",
+                file_string((gl_register_file) dstReg->File, mode),
                 dstReg->Index,
                 _mesa_writemask_string(dstReg->WriteMask));
 #endif
 }
 
+
 static void
-print_src_reg(const struct prog_src_register *srcReg, gl_prog_print_mode mode,
-              const struct gl_program *prog)
+fprint_src_reg(FILE *f,
+               const struct prog_src_register *srcReg, 
+               gl_prog_print_mode mode,
+               const struct gl_program *prog)
 {
-   _mesa_printf("%s%s",
-                reg_string((enum register_file) srcReg->File,
-                           srcReg->Index, mode, srcReg->RelAddr, prog),
-                _mesa_swizzle_string(srcReg->Swizzle,
-                                     srcReg->NegateBase, GL_FALSE));
+   const char *abs = srcReg->Abs ? "|" : "";
+
+   _mesa_fprintf(f, "%s%s%s%s",
+                 abs,
+                 reg_string((gl_register_file) srcReg->File,
+                            srcReg->Index, mode, srcReg->RelAddr, prog),
+                 _mesa_swizzle_string(srcReg->Swizzle,
+                                      srcReg->Negate, GL_FALSE),
+                 abs);
 #if 0
-   _mesa_printf("%s[%d]%s",
-                file_string((enum register_file) srcReg->File, mode),
-                srcReg->Index,
-                _mesa_swizzle_string(srcReg->Swizzle,
-                               srcReg->NegateBase, GL_FALSE));
+   _mesa_fprintf(f, "%s[%d]%s",
+                 file_string((gl_register_file) srcReg->File, mode),
+                 srcReg->Index,
+                 _mesa_swizzle_string(srcReg->Swizzle,
+                                      srcReg->Negate, GL_FALSE));
 #endif
 }
 
+
 static void
-print_comment(const struct prog_instruction *inst)
+fprint_comment(FILE *f, const struct prog_instruction *inst)
 {
    if (inst->Comment)
-      _mesa_printf(";  # %s\n", inst->Comment);
+      _mesa_fprintf(f, ";  # %s\n", inst->Comment);
    else
-      _mesa_printf(";\n");
+      _mesa_fprintf(f, ";\n");
 }
 
 
 static void
-print_alu_instruction(const struct prog_instruction *inst,
-                      const char *opcode_string, GLuint numRegs,
-                      gl_prog_print_mode mode,
-                      const struct gl_program *prog)
+fprint_alu_instruction(FILE *f,
+                       const struct prog_instruction *inst,
+                       const char *opcode_string, GLuint numRegs,
+                       gl_prog_print_mode mode,
+                       const struct gl_program *prog)
 {
    GLuint j;
 
-   _mesa_printf("%s", opcode_string);
+   _mesa_fprintf(f, "%s", opcode_string);
    if (inst->CondUpdate)
-      _mesa_printf(".C");
+      _mesa_fprintf(f, ".C");
 
    /* frag prog only */
    if (inst->SaturateMode == SATURATE_ZERO_ONE)
-      _mesa_printf("_SAT");
+      _mesa_fprintf(f, "_SAT");
 
-   _mesa_printf(" ");
+   _mesa_fprintf(f, " ");
    if (inst->DstReg.File != PROGRAM_UNDEFINED) {
-      print_dst_reg(&inst->DstReg, mode, prog);
+      fprint_dst_reg(f, &inst->DstReg, mode, prog);
    }
    else {
-      _mesa_printf(" ???");
+      _mesa_fprintf(f, " ???");
    }
 
    if (numRegs > 0)
-      _mesa_printf(", ");
+      _mesa_fprintf(f, ", ");
 
    for (j = 0; j < numRegs; j++) {
-      print_src_reg(inst->SrcReg + j, mode, prog);
+      fprint_src_reg(f, inst->SrcReg + j, mode, prog);
       if (j + 1 < numRegs)
-        _mesa_printf(", ");
+        _mesa_fprintf(f, ", ");
    }
 
-   print_comment(inst);
+   fprint_comment(f, inst);
 }
 
 
@@ -505,15 +533,8 @@ void
 _mesa_print_alu_instruction(const struct prog_instruction *inst,
                             const char *opcode_string, GLuint numRegs)
 {
-   print_alu_instruction(inst, opcode_string, numRegs, PROG_PRINT_DEBUG, NULL);
-}
-
-
-void
-_mesa_print_instruction(const struct prog_instruction *inst)
-{
-   /* note: 4th param should be ignored for PROG_PRINT_DEBUG */
-   _mesa_print_instruction_opt(inst, 0, PROG_PRINT_DEBUG, NULL);
+   fprint_alu_instruction(stdout, inst, opcode_string,
+                          numRegs, PROG_PRINT_DEBUG, NULL);
 }
 
 
@@ -521,7 +542,9 @@ _mesa_print_instruction(const struct prog_instruction *inst)
  * Print a single vertex/fragment program instruction.
  */
 GLint
-_mesa_print_instruction_opt(const struct prog_instruction *inst, GLint indent,
+_mesa_fprint_instruction_opt(FILE *f,
+                            const struct prog_instruction *inst,
+                            GLint indent,
                             gl_prog_print_mode mode,
                             const struct gl_program *prog)
 {
@@ -534,275 +557,321 @@ _mesa_print_instruction_opt(const struct prog_instruction *inst, GLint indent,
       indent -= 3;
    }
    for (i = 0; i < indent; i++) {
-      _mesa_printf(" ");
+      _mesa_fprintf(f, " ");
    }
 
    switch (inst->Opcode) {
    case OPCODE_PRINT:
-      _mesa_printf("PRINT '%s'", inst->Data);
+      _mesa_fprintf(f, "PRINT '%s'", inst->Data);
       if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) {
-         _mesa_printf(", ");
-         _mesa_printf("%s[%d]%s",
-                      file_string((enum register_file) inst->SrcReg[0].File,
+         _mesa_fprintf(f, ", ");
+         _mesa_fprintf(f, "%s[%d]%s",
+                      file_string((gl_register_file) inst->SrcReg[0].File,
                                   mode),
                       inst->SrcReg[0].Index,
                       _mesa_swizzle_string(inst->SrcReg[0].Swizzle,
-                                           inst->SrcReg[0].NegateBase, GL_FALSE));
+                                           inst->SrcReg[0].Negate, GL_FALSE));
       }
       if (inst->Comment)
-         _mesa_printf("  # %s", inst->Comment);
-      print_comment(inst);
+         _mesa_fprintf(f, "  # %s", inst->Comment);
+      fprint_comment(f, inst);
       break;
    case OPCODE_SWZ:
-      _mesa_printf("SWZ");
+      _mesa_fprintf(f, "SWZ");
       if (inst->SaturateMode == SATURATE_ZERO_ONE)
-         _mesa_printf("_SAT");
-      _mesa_printf(" ");
-      print_dst_reg(&inst->DstReg, mode, prog);
-      _mesa_printf(", %s[%d], %s",
-                   file_string((enum register_file) inst->SrcReg[0].File,
+         _mesa_fprintf(f, "_SAT");
+      _mesa_fprintf(f, " ");
+      fprint_dst_reg(f, &inst->DstReg, mode, prog);
+      _mesa_fprintf(f, ", %s[%d], %s",
+                   file_string((gl_register_file) inst->SrcReg[0].File,
                                mode),
                    inst->SrcReg[0].Index,
                    _mesa_swizzle_string(inst->SrcReg[0].Swizzle,
-                                        inst->SrcReg[0].NegateBase, GL_TRUE));
-      print_comment(inst);
+                                        inst->SrcReg[0].Negate, GL_TRUE));
+      fprint_comment(f, inst);
       break;
    case OPCODE_TEX:
    case OPCODE_TXP:
    case OPCODE_TXL:
    case OPCODE_TXB:
-      _mesa_printf("%s", _mesa_opcode_string(inst->Opcode));
+      _mesa_fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
       if (inst->SaturateMode == SATURATE_ZERO_ONE)
-         _mesa_printf("_SAT");
-      _mesa_printf(" ");
-      print_dst_reg(&inst->DstReg, mode, prog);
-      _mesa_printf(", ");
-      print_src_reg(&inst->SrcReg[0], mode, prog);
-      _mesa_printf(", texture[%d], ", inst->TexSrcUnit);
+         _mesa_fprintf(f, "_SAT");
+      _mesa_fprintf(f, " ");
+      fprint_dst_reg(f, &inst->DstReg, mode, prog);
+      _mesa_fprintf(f, ", ");
+      fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+      _mesa_fprintf(f, ", texture[%d], ", inst->TexSrcUnit);
       switch (inst->TexSrcTarget) {
-      case TEXTURE_1D_INDEX:   _mesa_printf("1D");    break;
-      case TEXTURE_2D_INDEX:   _mesa_printf("2D");    break;
-      case TEXTURE_3D_INDEX:   _mesa_printf("3D");    break;
-      case TEXTURE_CUBE_INDEX: _mesa_printf("CUBE");  break;
-      case TEXTURE_RECT_INDEX: _mesa_printf("RECT");  break;
+      case TEXTURE_1D_INDEX:   _mesa_fprintf(f, "1D");    break;
+      case TEXTURE_2D_INDEX:   _mesa_fprintf(f, "2D");    break;
+      case TEXTURE_3D_INDEX:   _mesa_fprintf(f, "3D");    break;
+      case TEXTURE_CUBE_INDEX: _mesa_fprintf(f, "CUBE");  break;
+      case TEXTURE_RECT_INDEX: _mesa_fprintf(f, "RECT");  break;
       default:
          ;
       }
-      print_comment(inst);
+      if (inst->TexShadow)
+         _mesa_fprintf(f, " SHADOW");
+      fprint_comment(f, inst);
       break;
 
    case OPCODE_KIL:
-      _mesa_printf("%s", _mesa_opcode_string(inst->Opcode));
-      _mesa_printf(" ");
-      print_src_reg(&inst->SrcReg[0], mode, prog);
-      print_comment(inst);
+      _mesa_fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
+      _mesa_fprintf(f, " ");
+      fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+      fprint_comment(f, inst);
       break;
    case OPCODE_KIL_NV:
-      _mesa_printf("%s", _mesa_opcode_string(inst->Opcode));
-      _mesa_printf(" ");
-      _mesa_printf("%s.%s",
+      _mesa_fprintf(f, "%s", _mesa_opcode_string(inst->Opcode));
+      _mesa_fprintf(f, " ");
+      _mesa_fprintf(f, "%s.%s",
                    _mesa_condcode_string(inst->DstReg.CondMask),
                    _mesa_swizzle_string(inst->DstReg.CondSwizzle,
                                         GL_FALSE, GL_FALSE));
-      print_comment(inst);
+      fprint_comment(f, inst);
       break;
 
    case OPCODE_ARL:
-      _mesa_printf("ARL ");
-      print_dst_reg(&inst->DstReg, mode, prog);
-      _mesa_printf(", ");
-      print_src_reg(&inst->SrcReg[0], mode, prog);
-      print_comment(inst);
+      _mesa_fprintf(f, "ARL ");
+      fprint_dst_reg(f, &inst->DstReg, mode, prog);
+      _mesa_fprintf(f, ", ");
+      fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+      fprint_comment(f, inst);
       break;
    case OPCODE_BRA:
-      _mesa_printf("BRA %d (%s%s)",
+      _mesa_fprintf(f, "BRA %d (%s%s)",
                    inst->BranchTarget,
                    _mesa_condcode_string(inst->DstReg.CondMask),
                    _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE));
-      print_comment(inst);
+      fprint_comment(f, inst);
       break;
    case OPCODE_IF:
       if (inst->SrcReg[0].File != PROGRAM_UNDEFINED) {
          /* Use ordinary register */
-         _mesa_printf("IF ");
-         print_src_reg(&inst->SrcReg[0], mode, prog);
-         _mesa_printf("; ");
+         _mesa_fprintf(f, "IF ");
+         fprint_src_reg(f, &inst->SrcReg[0], mode, prog);
+         _mesa_fprintf(f, "; ");
       }
       else {
          /* Use cond codes */
-         _mesa_printf("IF (%s%s);",
+         _mesa_fprintf(f, "IF (%s%s);",
                       _mesa_condcode_string(inst->DstReg.CondMask),
                       _mesa_swizzle_string(inst->DstReg.CondSwizzle,
                                            0, GL_FALSE));
       }
-      _mesa_printf(" # (if false, goto %d)", inst->BranchTarget);
-      print_comment(inst);
+      _mesa_fprintf(f, " # (if false, goto %d)", inst->BranchTarget);
+      fprint_comment(f, inst);
       return indent + 3;
    case OPCODE_ELSE:
-      _mesa_printf("ELSE; # (goto %d)\n", inst->BranchTarget);
+      _mesa_fprintf(f, "ELSE; # (goto %d)\n", inst->BranchTarget);
       return indent + 3;
    case OPCODE_ENDIF:
-      _mesa_printf("ENDIF;\n");
+      _mesa_fprintf(f, "ENDIF;\n");
       break;
    case OPCODE_BGNLOOP:
-      _mesa_printf("BGNLOOP; # (end at %d)\n", inst->BranchTarget);
+      _mesa_fprintf(f, "BGNLOOP; # (end at %d)\n", inst->BranchTarget);
       return indent + 3;
    case OPCODE_ENDLOOP:
-      _mesa_printf("ENDLOOP; # (goto %d)\n", inst->BranchTarget);
+      _mesa_fprintf(f, "ENDLOOP; # (goto %d)\n", inst->BranchTarget);
       break;
    case OPCODE_BRK:
    case OPCODE_CONT:
-      _mesa_printf("%s (%s%s); # (goto %d)",
+      _mesa_fprintf(f, "%s (%s%s); # (goto %d)",
                    _mesa_opcode_string(inst->Opcode),
                    _mesa_condcode_string(inst->DstReg.CondMask),
                    _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE),
                    inst->BranchTarget);
-      print_comment(inst);
+      fprint_comment(f, inst);
       break;
 
    case OPCODE_BGNSUB:
       if (mode == PROG_PRINT_NV) {
-         _mesa_printf("%s:\n", inst->Comment); /* comment is label */
+         _mesa_fprintf(f, "%s:\n", inst->Comment); /* comment is label */
          return indent;
       }
       else {
-         _mesa_printf("BGNSUB");
-         print_comment(inst);
+         _mesa_fprintf(f, "BGNSUB");
+         fprint_comment(f, inst);
          return indent + 3;
       }
    case OPCODE_ENDSUB:
       if (mode == PROG_PRINT_DEBUG) {
-         _mesa_printf("ENDSUB");
-         print_comment(inst);
+         _mesa_fprintf(f, "ENDSUB");
+         fprint_comment(f, inst);
       }
       break;
    case OPCODE_CAL:
       if (mode == PROG_PRINT_NV) {
-         _mesa_printf("CAL %s;  # (goto %d)\n", inst->Comment, inst->BranchTarget);
+         _mesa_fprintf(f, "CAL %s;  # (goto %d)\n", inst->Comment, inst->BranchTarget);
       }
       else {
-         _mesa_printf("CAL %u", inst->BranchTarget);
-         print_comment(inst);
+         _mesa_fprintf(f, "CAL %u", inst->BranchTarget);
+         fprint_comment(f, inst);
       }
       break;
    case OPCODE_RET:
-      _mesa_printf("RET (%s%s)",
+      _mesa_fprintf(f, "RET (%s%s)",
                    _mesa_condcode_string(inst->DstReg.CondMask),
                    _mesa_swizzle_string(inst->DstReg.CondSwizzle, 0, GL_FALSE));
-      print_comment(inst);
+      fprint_comment(f, inst);
       break;
 
    case OPCODE_END:
-      _mesa_printf("END\n");
+      _mesa_fprintf(f, "END\n");
       break;
    case OPCODE_NOP:
       if (mode == PROG_PRINT_DEBUG) {
-         _mesa_printf("NOP");
-         print_comment(inst);
+         _mesa_fprintf(f, "NOP");
+         fprint_comment(f, inst);
       }
       else if (inst->Comment) {
          /* ARB/NV extensions don't have NOP instruction */
-         _mesa_printf("# %s\n", inst->Comment);
+         _mesa_fprintf(f, "# %s\n", inst->Comment);
       }
       break;
    /* XXX may need other special-case instructions */
    default:
-      /* typical alu instruction */
-      print_alu_instruction(inst,
-                            _mesa_opcode_string(inst->Opcode),
-                            _mesa_num_inst_src_regs(inst->Opcode),
-                            mode, prog);
+      if (inst->Opcode < MAX_OPCODE) {
+         /* typical alu instruction */
+         fprint_alu_instruction(f, inst,
+                                _mesa_opcode_string(inst->Opcode),
+                                _mesa_num_inst_src_regs(inst->Opcode),
+                                mode, prog);
+      }
+      else {
+         fprint_alu_instruction(f, inst,
+                                _mesa_opcode_string(inst->Opcode),
+                                3/*_mesa_num_inst_src_regs(inst->Opcode)*/,
+                                mode, prog);
+      }
       break;
    }
    return indent;
 }
 
 
-/**
- * Print program to stdout, default options.
- */
+GLint
+_mesa_print_instruction_opt(const struct prog_instruction *inst,
+                            GLint indent,
+                            gl_prog_print_mode mode,
+                            const struct gl_program *prog)
+{
+   return _mesa_fprint_instruction_opt(stdout, inst, indent, mode, prog);
+}
+
+
 void
-_mesa_print_program(const struct gl_program *prog)
+_mesa_print_instruction(const struct prog_instruction *inst)
 {
-   _mesa_print_program_opt(prog, PROG_PRINT_DEBUG, GL_TRUE);
+   /* note: 4th param should be ignored for PROG_PRINT_DEBUG */
+   _mesa_fprint_instruction_opt(stdout, inst, 0, PROG_PRINT_DEBUG, NULL);
 }
 
 
+
 /**
  * Print program, with options.
  */
 void
-_mesa_print_program_opt(const struct gl_program *prog,
-                        gl_prog_print_mode mode,
-                        GLboolean lineNumbers)
+_mesa_fprint_program_opt(FILE *f,
+                         const struct gl_program *prog,
+                         gl_prog_print_mode mode,
+                         GLboolean lineNumbers)
 {
    GLuint i, indent = 0;
 
    switch (prog->Target) {
    case GL_VERTEX_PROGRAM_ARB:
       if (mode == PROG_PRINT_ARB)
-         _mesa_printf("!!ARBvp1.0\n");
+         _mesa_fprintf(f, "!!ARBvp1.0\n");
       else if (mode == PROG_PRINT_NV)
-         _mesa_printf("!!VP1.0\n");
+         _mesa_fprintf(f, "!!VP1.0\n");
       else
-         _mesa_printf("# Vertex Program/Shader\n");
+         _mesa_fprintf(f, "# Vertex Program/Shader\n");
       break;
    case GL_FRAGMENT_PROGRAM_ARB:
    case GL_FRAGMENT_PROGRAM_NV:
       if (mode == PROG_PRINT_ARB)
-         _mesa_printf("!!ARBfp1.0\n");
+         _mesa_fprintf(f, "!!ARBfp1.0\n");
       else if (mode == PROG_PRINT_NV)
-         _mesa_printf("!!FP1.0\n");
+         _mesa_fprintf(f, "!!FP1.0\n");
       else
-         _mesa_printf("# Fragment Program/Shader\n");
+         _mesa_fprintf(f, "# Fragment Program/Shader\n");
       break;
    }
 
    for (i = 0; i < prog->NumInstructions; i++) {
       if (lineNumbers)
-         _mesa_printf("%3d: ", i);
-      indent = _mesa_print_instruction_opt(prog->Instructions + i,
+         _mesa_fprintf(f, "%3d: ", i);
+      indent = _mesa_fprint_instruction_opt(f, prog->Instructions + i,
                                            indent, mode, prog);
    }
 }
 
 
 /**
- * Print all of a program's parameters.
+ * Print program to stdout, default options.
  */
 void
-_mesa_print_program_parameters(GLcontext *ctx, const struct gl_program *prog)
+_mesa_print_program(const struct gl_program *prog)
+{
+   _mesa_fprint_program_opt(stdout, prog, PROG_PRINT_DEBUG, GL_TRUE);
+}
+
+
+/**
+ * Print all of a program's parameters/fields to given file.
+ */
+static void
+_mesa_fprint_program_parameters(FILE *f,
+                                GLcontext *ctx,
+                                const struct gl_program *prog)
 {
    GLuint i;
 
-   _mesa_printf("InputsRead: 0x%x\n", prog->InputsRead);
-   _mesa_printf("OutputsWritten: 0x%x\n", prog->OutputsWritten);
-   _mesa_printf("NumInstructions=%d\n", prog->NumInstructions);
-   _mesa_printf("NumTemporaries=%d\n", prog->NumTemporaries);
-   _mesa_printf("NumParameters=%d\n", prog->NumParameters);
-   _mesa_printf("NumAttributes=%d\n", prog->NumAttributes);
-   _mesa_printf("NumAddressRegs=%d\n", prog->NumAddressRegs);
-   _mesa_printf("Samplers=[ ");
+   _mesa_fprintf(f, "InputsRead: 0x%x\n", prog->InputsRead);
+   _mesa_fprintf(f, "OutputsWritten: 0x%x\n", prog->OutputsWritten);
+   _mesa_fprintf(f, "NumInstructions=%d\n", prog->NumInstructions);
+   _mesa_fprintf(f, "NumTemporaries=%d\n", prog->NumTemporaries);
+   _mesa_fprintf(f, "NumParameters=%d\n", prog->NumParameters);
+   _mesa_fprintf(f, "NumAttributes=%d\n", prog->NumAttributes);
+   _mesa_fprintf(f, "NumAddressRegs=%d\n", prog->NumAddressRegs);
+   _mesa_fprintf(f, "Samplers=[ ");
    for (i = 0; i < MAX_SAMPLERS; i++) {
-      _mesa_printf("%d ", prog->SamplerUnits[i]);
+      _mesa_fprintf(f, "%d ", prog->SamplerUnits[i]);
    }
-   _mesa_printf("]\n");
+   _mesa_fprintf(f, "]\n");
 
    _mesa_load_state_parameters(ctx, prog->Parameters);
 
 #if 0
-   _mesa_printf("Local Params:\n");
+   _mesa_fprintf(f, "Local Params:\n");
    for (i = 0; i < MAX_PROGRAM_LOCAL_PARAMS; i++){
       const GLfloat *p = prog->LocalParams[i];
-      _mesa_printf("%2d: %f, %f, %f, %f\n", i, p[0], p[1], p[2], p[3]);
+      _mesa_fprintf(f, "%2d: %f, %f, %f, %f\n", i, p[0], p[1], p[2], p[3]);
    }
 #endif 
    _mesa_print_parameter_list(prog->Parameters);
 }
 
 
+/**
+ * Print all of a program's parameters/fields to stdout.
+ */
 void
-_mesa_print_parameter_list(const struct gl_program_parameter_list *list)
+_mesa_print_program_parameters(GLcontext *ctx, const struct gl_program *prog)
+{
+   _mesa_fprint_program_parameters(stdout, ctx, prog);
+}
+
+
+/**
+ * Print a program parameter list to given file.
+ */
+static void
+_mesa_fprint_parameter_list(FILE *f,
+                            const struct gl_program_parameter_list *list)
 {
    const gl_prog_print_mode mode = PROG_PRINT_DEBUG;
    GLuint i;
@@ -810,22 +879,114 @@ _mesa_print_parameter_list(const struct gl_program_parameter_list *list)
    if (!list)
       return;
 
-   _mesa_printf("param list %p\n", (void *) list);
+   _mesa_fprintf(f, "param list %p\n", (void *) list);
+   _mesa_fprintf(f, "dirty state flags: 0x%x\n", list->StateFlags);
    for (i = 0; i < list->NumParameters; i++){
       struct gl_program_parameter *param = list->Parameters + i;
       const GLfloat *v = list->ParameterValues[i];
-      _mesa_printf("param[%d] sz=%d %s %s = {%.3g, %.3g, %.3g, %.3g}",
+      _mesa_fprintf(f, "param[%d] sz=%d %s %s = {%.3g, %.3g, %.3g, %.3g}",
                    i, param->Size,
                    file_string(list->Parameters[i].Type, mode),
                    param->Name, v[0], v[1], v[2], v[3]);
-      if (param->Flags & PROG_PARAM_CENTROID_BIT)
-         _mesa_printf(" Centroid");
-      if (param->Flags & PROG_PARAM_INVARIANT_BIT)
-         _mesa_printf(" Invariant");
-      if (param->Flags & PROG_PARAM_FLAT_BIT)
-         _mesa_printf(" Flat");
-      if (param->Flags & PROG_PARAM_LINEAR_BIT)
-         _mesa_printf(" Linear");
-      _mesa_printf("\n");
+      if (param->Flags & PROG_PARAM_BIT_CENTROID)
+         _mesa_fprintf(f, " Centroid");
+      if (param->Flags & PROG_PARAM_BIT_INVARIANT)
+         _mesa_fprintf(f, " Invariant");
+      if (param->Flags & PROG_PARAM_BIT_FLAT)
+         _mesa_fprintf(f, " Flat");
+      if (param->Flags & PROG_PARAM_BIT_LINEAR)
+         _mesa_fprintf(f, " Linear");
+      _mesa_fprintf(f, "\n");
    }
 }
+
+
+/**
+ * Print a program parameter list to stdout.
+ */
+void
+_mesa_print_parameter_list(const struct gl_program_parameter_list *list)
+{
+   _mesa_fprint_parameter_list(stdout, list);
+}
+
+
+/**
+ * Write shader and associated info to a file.
+ */
+void
+_mesa_write_shader_to_file(const struct gl_shader *shader)
+{
+   const char *type;
+   char filename[100];
+   FILE *f;
+
+   if (shader->Type == GL_FRAGMENT_SHADER)
+      type = "frag";
+   else
+      type = "vert";
+
+   _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type);
+   f = fopen(filename, "w");
+   if (!f) {
+      fprintf(stderr, "Unable to open %s for writing\n", filename);
+      return;
+   }
+
+   fprintf(f, "/* Shader %u source, checksum %u */\n", shader->Name, shader->SourceChecksum);
+   fputs(shader->Source, f);
+   fprintf(f, "\n");
+
+   fprintf(f, "/* Compile status: %s */\n",
+           shader->CompileStatus ? "ok" : "fail");
+   if (!shader->CompileStatus) {
+      fprintf(f, "/* Log Info: */\n");
+      fputs(shader->InfoLog, f);
+   }
+   else {
+      fprintf(f, "/* GPU code */\n");
+      fprintf(f, "/*\n");
+      _mesa_fprint_program_opt(f, shader->Program, PROG_PRINT_DEBUG, GL_TRUE);
+      fprintf(f, "*/\n");
+      fprintf(f, "/* Parameters / constants */\n");
+      fprintf(f, "/*\n");
+      _mesa_fprint_parameter_list(f, shader->Program->Parameters);
+      fprintf(f, "*/\n");
+   }
+
+   fclose(f);
+}
+
+
+/**
+ * Append the shader's uniform info/values to the shader log file.
+ * The log file will typically have been created by the
+ * _mesa_write_shader_to_file function.
+ */
+void
+_mesa_append_uniforms_to_file(const struct gl_shader *shader,
+                              const struct gl_program *prog)
+{
+   const char *type;
+   char filename[100];
+   FILE *f;
+
+   if (shader->Type == GL_FRAGMENT_SHADER)
+      type = "frag";
+   else
+      type = "vert";
+
+   _mesa_snprintf(filename, sizeof(filename), "shader_%u.%s", shader->Name, type);
+   f = fopen(filename, "a"); /* append */
+   if (!f) {
+      fprintf(stderr, "Unable to open %s for appending\n", filename);
+      return;
+   }
+
+   fprintf(f, "/* First-draw parameters / constants */\n");
+   fprintf(f, "/*\n");
+   _mesa_fprint_parameter_list(f, prog->Parameters);
+   fprintf(f, "*/\n");
+
+   fclose(f);
+}