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
_mesa_insert_mvp_dp4_code(struct gl_context * ctx,struct gl_vertex_program * vprog)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
_mesa_insert_mvp_mad_code(struct gl_context * ctx,struct gl_vertex_program * vprog)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
_mesa_insert_mvp_code(struct gl_context * ctx,struct gl_vertex_program * vprog)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
_mesa_append_fog_code(struct gl_context * ctx,struct gl_fragment_program * fprog,GLenum fog_mode,GLboolean saturate)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
is_texture_instruction(const struct prog_instruction * inst)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
_mesa_count_texture_indirections(struct gl_program * prog)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
_mesa_count_texture_instructions(struct gl_program * prog)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
_mesa_remove_output_reads(struct gl_program * prog,gl_register_file type)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
_mesa_nop_fragment_program(struct gl_context * ctx,struct gl_fragment_program * prog)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
_mesa_nop_vertex_program(struct gl_context * ctx,struct gl_vertex_program * prog)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