Home | History | Annotate | Download | only in program
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included
     14  * in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 /**
     26  * \file  programopt.c
     27  * Vertex/Fragment program optimizations and transformations for program
     28  * options, etc.
     29  *
     30  * \author Brian Paul
     31  */
     32 
     33 
     34 #include "main/glheader.h"
     35 #include "main/context.h"
     36 #include "prog_parameter.h"
     37 #include "prog_statevars.h"
     38 #include "program.h"
     39 #include "programopt.h"
     40 #include "prog_instruction.h"
     41 
     42 
     43 /**
     44  * This function inserts instructions for coordinate modelview * projection
     45  * into a vertex program.
     46  * May be used to implement the position_invariant option.
     47  */
     48 static void
     49 _mesa_insert_mvp_dp4_code(struct gl_context *ctx, struct gl_program *vprog)
     50 {
     51    struct prog_instruction *newInst;
     52    const GLuint origLen = vprog->arb.NumInstructions;
     53    const GLuint newLen = origLen + 4;
     54    GLuint i;
     55 
     56    /*
     57     * Setup state references for the modelview/projection matrix.
     58     * XXX we should check if these state vars are already declared.
     59     */
     60    static const gl_state_index mvpState[4][STATE_LENGTH] = {
     61       { STATE_MVP_MATRIX, 0, 0, 0, 0 },  /* state.matrix.mvp.row[0] */
     62       { STATE_MVP_MATRIX, 0, 1, 1, 0 },  /* state.matrix.mvp.row[1] */
     63       { STATE_MVP_MATRIX, 0, 2, 2, 0 },  /* state.matrix.mvp.row[2] */
     64       { STATE_MVP_MATRIX, 0, 3, 3, 0 },  /* state.matrix.mvp.row[3] */
     65    };
     66    GLint mvpRef[4];
     67 
     68    for (i = 0; i < 4; i++) {
     69       mvpRef[i] = _mesa_add_state_reference(vprog->Parameters, mvpState[i]);
     70    }
     71 
     72    /* Alloc storage for new instructions */
     73    newInst = rzalloc_array(vprog, struct prog_instruction, newLen);
     74    if (!newInst) {
     75       _mesa_error(ctx, GL_OUT_OF_MEMORY,
     76                   "glProgramString(inserting position_invariant code)");
     77       return;
     78    }
     79 
     80    /*
     81     * Generated instructions:
     82     * newInst[0] = DP4 result.position.x, mvp.row[0], vertex.position;
     83     * newInst[1] = DP4 result.position.y, mvp.row[1], vertex.position;
     84     * newInst[2] = DP4 result.position.z, mvp.row[2], vertex.position;
     85     * newInst[3] = DP4 result.position.w, mvp.row[3], vertex.position;
     86     */
     87    _mesa_init_instructions(newInst, 4);
     88    for (i = 0; i < 4; i++) {
     89       newInst[i].Opcode = OPCODE_DP4;
     90       newInst[i].DstReg.File = PROGRAM_OUTPUT;
     91       newInst[i].DstReg.Index = VARYING_SLOT_POS;
     92       newInst[i].DstReg.WriteMask = (WRITEMASK_X << i);
     93       newInst[i].SrcReg[0].File = PROGRAM_STATE_VAR;
     94       newInst[i].SrcReg[0].Index = mvpRef[i];
     95       newInst[i].SrcReg[0].Swizzle = SWIZZLE_NOOP;
     96       newInst[i].SrcReg[1].File = PROGRAM_INPUT;
     97       newInst[i].SrcReg[1].Index = VERT_ATTRIB_POS;
     98       newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP;
     99    }
    100 
    101    /* Append original instructions after new instructions */
    102    _mesa_copy_instructions (newInst + 4, vprog->arb.Instructions, origLen);
    103 
    104    /* free old instructions */
    105    ralloc_free(vprog->arb.Instructions);
    106 
    107    /* install new instructions */
    108    vprog->arb.Instructions = newInst;
    109    vprog->arb.NumInstructions = newLen;
    110    vprog->info.inputs_read |= VERT_BIT_POS;
    111    vprog->info.outputs_written |= BITFIELD64_BIT(VARYING_SLOT_POS);
    112 }
    113 
    114 
    115 static void
    116 _mesa_insert_mvp_mad_code(struct gl_context *ctx, struct gl_program *vprog)
    117 {
    118    struct prog_instruction *newInst;
    119    const GLuint origLen = vprog->arb.NumInstructions;
    120    const GLuint newLen = origLen + 4;
    121    GLuint hposTemp;
    122    GLuint i;
    123 
    124    /*
    125     * Setup state references for the modelview/projection matrix.
    126     * XXX we should check if these state vars are already declared.
    127     */
    128    static const gl_state_index mvpState[4][STATE_LENGTH] = {
    129       { STATE_MVP_MATRIX, 0, 0, 0, STATE_MATRIX_TRANSPOSE },
    130       { STATE_MVP_MATRIX, 0, 1, 1, STATE_MATRIX_TRANSPOSE },
    131       { STATE_MVP_MATRIX, 0, 2, 2, STATE_MATRIX_TRANSPOSE },
    132       { STATE_MVP_MATRIX, 0, 3, 3, STATE_MATRIX_TRANSPOSE },
    133    };
    134    GLint mvpRef[4];
    135 
    136    for (i = 0; i < 4; i++) {
    137       mvpRef[i] = _mesa_add_state_reference(vprog->Parameters, mvpState[i]);
    138    }
    139 
    140    /* Alloc storage for new instructions */
    141    newInst = rzalloc_array(vprog, struct prog_instruction, newLen);
    142    if (!newInst) {
    143       _mesa_error(ctx, GL_OUT_OF_MEMORY,
    144                   "glProgramString(inserting position_invariant code)");
    145       return;
    146    }
    147 
    148    /* TEMP hposTemp; */
    149    hposTemp = vprog->arb.NumTemporaries++;
    150 
    151    /*
    152     * Generated instructions:
    153     *    emit_op2(p, OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]);
    154     *    emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp);
    155     *    emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp);
    156     *    emit_op3(p, OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp);
    157     */
    158    _mesa_init_instructions(newInst, 4);
    159 
    160    newInst[0].Opcode = OPCODE_MUL;
    161    newInst[0].DstReg.File = PROGRAM_TEMPORARY;
    162    newInst[0].DstReg.Index = hposTemp;
    163    newInst[0].DstReg.WriteMask = WRITEMASK_XYZW;
    164    newInst[0].SrcReg[0].File = PROGRAM_INPUT;
    165    newInst[0].SrcReg[0].Index = VERT_ATTRIB_POS;
    166    newInst[0].SrcReg[0].Swizzle = SWIZZLE_XXXX;
    167    newInst[0].SrcReg[1].File = PROGRAM_STATE_VAR;
    168    newInst[0].SrcReg[1].Index = mvpRef[0];
    169    newInst[0].SrcReg[1].Swizzle = SWIZZLE_NOOP;
    170 
    171    for (i = 1; i <= 2; i++) {
    172       newInst[i].Opcode = OPCODE_MAD;
    173       newInst[i].DstReg.File = PROGRAM_TEMPORARY;
    174       newInst[i].DstReg.Index = hposTemp;
    175       newInst[i].DstReg.WriteMask = WRITEMASK_XYZW;
    176       newInst[i].SrcReg[0].File = PROGRAM_INPUT;
    177       newInst[i].SrcReg[0].Index = VERT_ATTRIB_POS;
    178       newInst[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(i,i,i,i);
    179       newInst[i].SrcReg[1].File = PROGRAM_STATE_VAR;
    180       newInst[i].SrcReg[1].Index = mvpRef[i];
    181       newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP;
    182       newInst[i].SrcReg[2].File = PROGRAM_TEMPORARY;
    183       newInst[i].SrcReg[2].Index = hposTemp;
    184       newInst[1].SrcReg[2].Swizzle = SWIZZLE_NOOP;
    185    }
    186 
    187    newInst[3].Opcode = OPCODE_MAD;
    188    newInst[3].DstReg.File = PROGRAM_OUTPUT;
    189    newInst[3].DstReg.Index = VARYING_SLOT_POS;
    190    newInst[3].DstReg.WriteMask = WRITEMASK_XYZW;
    191    newInst[3].SrcReg[0].File = PROGRAM_INPUT;
    192    newInst[3].SrcReg[0].Index = VERT_ATTRIB_POS;
    193    newInst[3].SrcReg[0].Swizzle = SWIZZLE_WWWW;
    194    newInst[3].SrcReg[1].File = PROGRAM_STATE_VAR;
    195    newInst[3].SrcReg[1].Index = mvpRef[3];
    196    newInst[3].SrcReg[1].Swizzle = SWIZZLE_NOOP;
    197    newInst[3].SrcReg[2].File = PROGRAM_TEMPORARY;
    198    newInst[3].SrcReg[2].Index = hposTemp;
    199    newInst[3].SrcReg[2].Swizzle = SWIZZLE_NOOP;
    200 
    201 
    202    /* Append original instructions after new instructions */
    203    _mesa_copy_instructions (newInst + 4, vprog->arb.Instructions, origLen);
    204 
    205    /* free old instructions */
    206    ralloc_free(vprog->arb.Instructions);
    207 
    208    /* install new instructions */
    209    vprog->arb.Instructions = newInst;
    210    vprog->arb.NumInstructions = newLen;
    211    vprog->info.inputs_read |= VERT_BIT_POS;
    212    vprog->info.outputs_written |= BITFIELD64_BIT(VARYING_SLOT_POS);
    213 }
    214 
    215 
    216 void
    217 _mesa_insert_mvp_code(struct gl_context *ctx, struct gl_program *vprog)
    218 {
    219    if (ctx->Const.ShaderCompilerOptions[MESA_SHADER_VERTEX].OptimizeForAOS)
    220       _mesa_insert_mvp_dp4_code( ctx, vprog );
    221    else
    222       _mesa_insert_mvp_mad_code( ctx, vprog );
    223 }
    224 
    225 
    226 
    227 
    228 
    229 
    230 /**
    231  * Append instructions to implement fog
    232  *
    233  * The \c fragment.fogcoord input is used to compute the fog blend factor.
    234  *
    235  * \param ctx      The GL context
    236  * \param fprog    Fragment program that fog instructions will be appended to.
    237  * \param fog_mode Fog mode.  One of \c GL_EXP, \c GL_EXP2, or \c GL_LINEAR.
    238  * \param saturate True if writes to color outputs should be clamped to [0, 1]
    239  *
    240  * \note
    241  * This function sets \c VARYING_BIT_FOGC in \c fprog->info.inputs_read.
    242  *
    243  * \todo With a little work, this function could be adapted to add fog code
    244  * to vertex programs too.
    245  */
    246 void
    247 _mesa_append_fog_code(struct gl_context *ctx, struct gl_program *fprog,
    248                       GLenum fog_mode, GLboolean saturate)
    249 {
    250    static const gl_state_index fogPStateOpt[STATE_LENGTH]
    251       = { STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED, 0, 0, 0 };
    252    static const gl_state_index fogColorState[STATE_LENGTH]
    253       = { STATE_FOG_COLOR, 0, 0, 0, 0};
    254    struct prog_instruction *newInst, *inst;
    255    const GLuint origLen = fprog->arb.NumInstructions;
    256    const GLuint newLen = origLen + 5;
    257    GLuint i;
    258    GLint fogPRefOpt, fogColorRef; /* state references */
    259    GLuint colorTemp, fogFactorTemp; /* temporary registerss */
    260 
    261    if (fog_mode == GL_NONE) {
    262       _mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program"
    263                     " with fog_mode == GL_NONE");
    264       return;
    265    }
    266 
    267    if (!(fprog->info.outputs_written & (1 << FRAG_RESULT_COLOR))) {
    268       /* program doesn't output color, so nothing to do */
    269       return;
    270    }
    271 
    272    /* Alloc storage for new instructions */
    273    newInst = rzalloc_array(fprog, struct prog_instruction, newLen);
    274    if (!newInst) {
    275       _mesa_error(ctx, GL_OUT_OF_MEMORY,
    276                   "glProgramString(inserting fog_option code)");
    277       return;
    278    }
    279 
    280    /* Copy orig instructions into new instruction buffer */
    281    _mesa_copy_instructions(newInst, fprog->arb.Instructions, origLen);
    282 
    283    /* PARAM fogParamsRefOpt = internal optimized fog params; */
    284    fogPRefOpt
    285       = _mesa_add_state_reference(fprog->Parameters, fogPStateOpt);
    286    /* PARAM fogColorRef = state.fog.color; */
    287    fogColorRef
    288       = _mesa_add_state_reference(fprog->Parameters, fogColorState);
    289 
    290    /* TEMP colorTemp; */
    291    colorTemp = fprog->arb.NumTemporaries++;
    292    /* TEMP fogFactorTemp; */
    293    fogFactorTemp = fprog->arb.NumTemporaries++;
    294 
    295    /* Scan program to find where result.color is written */
    296    inst = newInst;
    297    for (i = 0; i < fprog->arb.NumInstructions; i++) {
    298       if (inst->Opcode == OPCODE_END)
    299          break;
    300       if (inst->DstReg.File == PROGRAM_OUTPUT &&
    301           inst->DstReg.Index == FRAG_RESULT_COLOR) {
    302          /* change the instruction to write to colorTemp w/ clamping */
    303          inst->DstReg.File = PROGRAM_TEMPORARY;
    304          inst->DstReg.Index = colorTemp;
    305          inst->Saturate = saturate;
    306          /* don't break (may be several writes to result.color) */
    307       }
    308       inst++;
    309    }
    310    assert(inst->Opcode == OPCODE_END); /* we'll overwrite this inst */
    311 
    312    _mesa_init_instructions(inst, 5);
    313 
    314    /* emit instructions to compute fog blending factor */
    315    /* this is always clamped to [0, 1] regardless of fragment clamping */
    316    if (fog_mode == GL_LINEAR) {
    317       /* MAD fogFactorTemp.x, fragment.fogcoord.x, fogPRefOpt.x, fogPRefOpt.y; */
    318       inst->Opcode = OPCODE_MAD;
    319       inst->DstReg.File = PROGRAM_TEMPORARY;
    320       inst->DstReg.Index = fogFactorTemp;
    321       inst->DstReg.WriteMask = WRITEMASK_X;
    322       inst->SrcReg[0].File = PROGRAM_INPUT;
    323       inst->SrcReg[0].Index = VARYING_SLOT_FOGC;
    324       inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
    325       inst->SrcReg[1].File = PROGRAM_STATE_VAR;
    326       inst->SrcReg[1].Index = fogPRefOpt;
    327       inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
    328       inst->SrcReg[2].File = PROGRAM_STATE_VAR;
    329       inst->SrcReg[2].Index = fogPRefOpt;
    330       inst->SrcReg[2].Swizzle = SWIZZLE_YYYY;
    331       inst->Saturate = GL_TRUE;
    332       inst++;
    333    }
    334    else {
    335       assert(fog_mode == GL_EXP || fog_mode == GL_EXP2);
    336       /* fogPRefOpt.z = d/ln(2), fogPRefOpt.w = d/sqrt(ln(2) */
    337       /* EXP: MUL fogFactorTemp.x, fogPRefOpt.z, fragment.fogcoord.x; */
    338       /* EXP2: MUL fogFactorTemp.x, fogPRefOpt.w, fragment.fogcoord.x; */
    339       inst->Opcode = OPCODE_MUL;
    340       inst->DstReg.File = PROGRAM_TEMPORARY;
    341       inst->DstReg.Index = fogFactorTemp;
    342       inst->DstReg.WriteMask = WRITEMASK_X;
    343       inst->SrcReg[0].File = PROGRAM_STATE_VAR;
    344       inst->SrcReg[0].Index = fogPRefOpt;
    345       inst->SrcReg[0].Swizzle
    346          = (fog_mode == GL_EXP) ? SWIZZLE_ZZZZ : SWIZZLE_WWWW;
    347       inst->SrcReg[1].File = PROGRAM_INPUT;
    348       inst->SrcReg[1].Index = VARYING_SLOT_FOGC;
    349       inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
    350       inst++;
    351       if (fog_mode == GL_EXP2) {
    352          /* MUL fogFactorTemp.x, fogFactorTemp.x, fogFactorTemp.x; */
    353          inst->Opcode = OPCODE_MUL;
    354          inst->DstReg.File = PROGRAM_TEMPORARY;
    355          inst->DstReg.Index = fogFactorTemp;
    356          inst->DstReg.WriteMask = WRITEMASK_X;
    357          inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    358          inst->SrcReg[0].Index = fogFactorTemp;
    359          inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
    360          inst->SrcReg[1].File = PROGRAM_TEMPORARY;
    361          inst->SrcReg[1].Index = fogFactorTemp;
    362          inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
    363          inst++;
    364       }
    365       /* EX2_SAT fogFactorTemp.x, -fogFactorTemp.x; */
    366       inst->Opcode = OPCODE_EX2;
    367       inst->DstReg.File = PROGRAM_TEMPORARY;
    368       inst->DstReg.Index = fogFactorTemp;
    369       inst->DstReg.WriteMask = WRITEMASK_X;
    370       inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    371       inst->SrcReg[0].Index = fogFactorTemp;
    372       inst->SrcReg[0].Negate = NEGATE_XYZW;
    373       inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
    374       inst->Saturate = GL_TRUE;
    375       inst++;
    376    }
    377    /* LRP result.color.xyz, fogFactorTemp.xxxx, colorTemp, fogColorRef; */
    378    inst->Opcode = OPCODE_LRP;
    379    inst->DstReg.File = PROGRAM_OUTPUT;
    380    inst->DstReg.Index = FRAG_RESULT_COLOR;
    381    inst->DstReg.WriteMask = WRITEMASK_XYZ;
    382    inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    383    inst->SrcReg[0].Index = fogFactorTemp;
    384    inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
    385    inst->SrcReg[1].File = PROGRAM_TEMPORARY;
    386    inst->SrcReg[1].Index = colorTemp;
    387    inst->SrcReg[1].Swizzle = SWIZZLE_NOOP;
    388    inst->SrcReg[2].File = PROGRAM_STATE_VAR;
    389    inst->SrcReg[2].Index = fogColorRef;
    390    inst->SrcReg[2].Swizzle = SWIZZLE_NOOP;
    391    inst++;
    392    /* MOV result.color.w, colorTemp.x;  # copy alpha */
    393    inst->Opcode = OPCODE_MOV;
    394    inst->DstReg.File = PROGRAM_OUTPUT;
    395    inst->DstReg.Index = FRAG_RESULT_COLOR;
    396    inst->DstReg.WriteMask = WRITEMASK_W;
    397    inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    398    inst->SrcReg[0].Index = colorTemp;
    399    inst->SrcReg[0].Swizzle = SWIZZLE_NOOP;
    400    inst++;
    401    /* END; */
    402    inst->Opcode = OPCODE_END;
    403    inst++;
    404 
    405    /* free old instructions */
    406    ralloc_free(fprog->arb.Instructions);
    407 
    408    /* install new instructions */
    409    fprog->arb.Instructions = newInst;
    410    fprog->arb.NumInstructions = inst - newInst;
    411    fprog->info.inputs_read |= VARYING_BIT_FOGC;
    412    assert(fprog->info.outputs_written & (1 << FRAG_RESULT_COLOR));
    413 }
    414 
    415 
    416 
    417 static GLboolean
    418 is_texture_instruction(const struct prog_instruction *inst)
    419 {
    420    switch (inst->Opcode) {
    421    case OPCODE_TEX:
    422    case OPCODE_TXB:
    423    case OPCODE_TXD:
    424    case OPCODE_TXL:
    425    case OPCODE_TXP:
    426       return GL_TRUE;
    427    default:
    428       return GL_FALSE;
    429    }
    430 }
    431 
    432 
    433 /**
    434  * Count the number of texure indirections in the given program.
    435  * The program's NumTexIndirections field will be updated.
    436  * See the GL_ARB_fragment_program spec (issue 24) for details.
    437  * XXX we count texture indirections in texenvprogram.c (maybe use this code
    438  * instead and elsewhere).
    439  */
    440 void
    441 _mesa_count_texture_indirections(struct gl_program *prog)
    442 {
    443    GLuint indirections = 1;
    444    GLbitfield tempsOutput = 0x0;
    445    GLbitfield aluTemps = 0x0;
    446    GLuint i;
    447 
    448    for (i = 0; i < prog->arb.NumInstructions; i++) {
    449       const struct prog_instruction *inst = prog->arb.Instructions + i;
    450 
    451       if (is_texture_instruction(inst)) {
    452          if (((inst->SrcReg[0].File == PROGRAM_TEMPORARY) &&
    453               (tempsOutput & (1 << inst->SrcReg[0].Index))) ||
    454              ((inst->Opcode != OPCODE_KIL) &&
    455               (inst->DstReg.File == PROGRAM_TEMPORARY) &&
    456               (aluTemps & (1 << inst->DstReg.Index))))
    457             {
    458                indirections++;
    459                tempsOutput = 0x0;
    460                aluTemps = 0x0;
    461             }
    462       }
    463       else {
    464          GLuint j;
    465          for (j = 0; j < 3; j++) {
    466             if (inst->SrcReg[j].File == PROGRAM_TEMPORARY)
    467                aluTemps |= (1 << inst->SrcReg[j].Index);
    468          }
    469          if (inst->DstReg.File == PROGRAM_TEMPORARY)
    470             aluTemps |= (1 << inst->DstReg.Index);
    471       }
    472 
    473       if ((inst->Opcode != OPCODE_KIL) && (inst->DstReg.File == PROGRAM_TEMPORARY))
    474          tempsOutput |= (1 << inst->DstReg.Index);
    475    }
    476 
    477    prog->arb.NumTexIndirections = indirections;
    478 }
    479 
    480 
    481 /**
    482  * Count number of texture instructions in given program and update the
    483  * program's NumTexInstructions field.
    484  */
    485 void
    486 _mesa_count_texture_instructions(struct gl_program *prog)
    487 {
    488    GLuint i;
    489    prog->arb.NumTexInstructions = 0;
    490    for (i = 0; i < prog->arb.NumInstructions; i++) {
    491       prog->arb.NumTexInstructions +=
    492          is_texture_instruction(prog->arb.Instructions + i);
    493    }
    494 }
    495 
    496 
    497 /**
    498  * Scan/rewrite program to remove reads of custom (output) registers.
    499  * The passed type has to be PROGRAM_OUTPUT.
    500  * On some hardware, trying to read an output register causes trouble.
    501  * So, rewrite the program to use a temporary register in this case.
    502  */
    503 void
    504 _mesa_remove_output_reads(struct gl_program *prog, gl_register_file type)
    505 {
    506    GLuint i;
    507    GLint outputMap[VARYING_SLOT_MAX];
    508    GLuint numVaryingReads = 0;
    509    GLboolean usedTemps[MAX_PROGRAM_TEMPS];
    510    GLuint firstTemp = 0;
    511 
    512    _mesa_find_used_registers(prog, PROGRAM_TEMPORARY,
    513                              usedTemps, MAX_PROGRAM_TEMPS);
    514 
    515    assert(type == PROGRAM_OUTPUT);
    516 
    517    for (i = 0; i < VARYING_SLOT_MAX; i++)
    518       outputMap[i] = -1;
    519 
    520    /* look for instructions which read from varying vars */
    521    for (i = 0; i < prog->arb.NumInstructions; i++) {
    522       struct prog_instruction *inst = prog->arb.Instructions + i;
    523       const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
    524       GLuint j;
    525       for (j = 0; j < numSrc; j++) {
    526          if (inst->SrcReg[j].File == type) {
    527             /* replace the read with a temp reg */
    528             const GLuint var = inst->SrcReg[j].Index;
    529             if (outputMap[var] == -1) {
    530                numVaryingReads++;
    531                outputMap[var] = _mesa_find_free_register(usedTemps,
    532                                                          MAX_PROGRAM_TEMPS,
    533                                                          firstTemp);
    534                firstTemp = outputMap[var] + 1;
    535             }
    536             inst->SrcReg[j].File = PROGRAM_TEMPORARY;
    537             inst->SrcReg[j].Index = outputMap[var];
    538          }
    539       }
    540    }
    541 
    542    if (numVaryingReads == 0)
    543       return; /* nothing to be done */
    544 
    545    /* look for instructions which write to the varying vars identified above */
    546    for (i = 0; i < prog->arb.NumInstructions; i++) {
    547       struct prog_instruction *inst = prog->arb.Instructions + i;
    548       if (inst->DstReg.File == type &&
    549           outputMap[inst->DstReg.Index] >= 0) {
    550          /* change inst to write to the temp reg, instead of the varying */
    551          inst->DstReg.File = PROGRAM_TEMPORARY;
    552          inst->DstReg.Index = outputMap[inst->DstReg.Index];
    553       }
    554    }
    555 
    556    /* insert new instructions to copy the temp vars to the varying vars */
    557    {
    558       struct prog_instruction *inst;
    559       GLint endPos, var;
    560 
    561       /* Look for END instruction and insert the new varying writes */
    562       endPos = -1;
    563       for (i = 0; i < prog->arb.NumInstructions; i++) {
    564          struct prog_instruction *inst = prog->arb.Instructions + i;
    565          if (inst->Opcode == OPCODE_END) {
    566             endPos = i;
    567             _mesa_insert_instructions(prog, i, numVaryingReads);
    568             break;
    569          }
    570       }
    571 
    572       assert(endPos >= 0);
    573 
    574       /* insert new MOV instructions here */
    575       inst = prog->arb.Instructions + endPos;
    576       for (var = 0; var < VARYING_SLOT_MAX; var++) {
    577          if (outputMap[var] >= 0) {
    578             /* MOV VAR[var], TEMP[tmp]; */
    579             inst->Opcode = OPCODE_MOV;
    580             inst->DstReg.File = type;
    581             inst->DstReg.Index = var;
    582             inst->SrcReg[0].File = PROGRAM_TEMPORARY;
    583             inst->SrcReg[0].Index = outputMap[var];
    584             inst++;
    585          }
    586       }
    587    }
    588 }
    589 
    590 void
    591 _mesa_program_fragment_position_to_sysval(struct gl_program *prog)
    592 {
    593    GLuint i;
    594 
    595    if (prog->Target != GL_FRAGMENT_PROGRAM_ARB ||
    596        !(prog->info.inputs_read & BITFIELD64_BIT(VARYING_SLOT_POS)))
    597       return;
    598 
    599    prog->info.inputs_read &= ~BITFIELD64_BIT(VARYING_SLOT_POS);
    600    prog->info.system_values_read |= 1 << SYSTEM_VALUE_FRAG_COORD;
    601 
    602    for (i = 0; i < prog->arb.NumInstructions; i++) {
    603       struct prog_instruction *inst = prog->arb.Instructions + i;
    604       const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
    605       GLuint j;
    606 
    607       for (j = 0; j < numSrc; j++) {
    608          if (inst->SrcReg[j].File == PROGRAM_INPUT &&
    609              inst->SrcReg[j].Index == VARYING_SLOT_POS) {
    610             inst->SrcReg[j].File = PROGRAM_SYSTEM_VALUE;
    611             inst->SrcReg[j].Index = SYSTEM_VALUE_FRAG_COORD;
    612          }
    613       }
    614    }
    615 }
    616