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