1/*
2 Copyright (C) Intel Corp.  2006.  All Rights Reserved.
3 Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
4 develop this 3D driver.
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice (including the
15 next paragraph) shall be included in all copies or substantial
16 portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **********************************************************************/
27 /*
28  * Authors:
29  *   Keith Whitwell <keith@tungstengraphics.com>
30  */
31
32
33#include "brw_context.h"
34#include "brw_wm.h"
35
36
37static GLuint get_tracked_mask(struct brw_wm_compile *c,
38			       struct brw_wm_instruction *inst)
39{
40   GLuint i;
41   for (i = 0; i < 4; i++) {
42      if (inst->writemask & (1<<i)) {
43	 if (!inst->dst[i]->contributes_to_output) {
44	    inst->writemask &= ~(1<<i);
45	    inst->dst[i] = 0;
46	 }
47      }
48   }
49
50   return inst->writemask;
51}
52
53/* Remove a reference from a value's usage chain.
54 */
55static void unlink_ref(struct brw_wm_ref *ref)
56{
57   struct brw_wm_value *value = ref->value;
58
59   if (ref == value->lastuse) {
60      value->lastuse = ref->prevuse;
61   }
62   else {
63      struct brw_wm_ref *i = value->lastuse;
64      while (i->prevuse != ref) i = i->prevuse;
65      i->prevuse = ref->prevuse;
66   }
67}
68
69static void track_arg(struct brw_wm_compile *c,
70		      struct brw_wm_instruction *inst,
71		      GLuint arg,
72		      GLuint readmask)
73{
74   GLuint i;
75
76   for (i = 0; i < 4; i++) {
77      struct brw_wm_ref *ref = inst->src[arg][i];
78      if (ref) {
79	 if (readmask & (1<<i)) {
80	    ref->value->contributes_to_output = 1;
81         }
82	 else {
83	    unlink_ref(ref);
84	    inst->src[arg][i] = NULL;
85	 }
86      }
87   }
88}
89
90static GLuint get_texcoord_mask( GLuint tex_idx )
91{
92   switch (tex_idx) {
93   case TEXTURE_1D_INDEX:
94      return WRITEMASK_X;
95   case TEXTURE_2D_INDEX:
96   case TEXTURE_1D_ARRAY_INDEX:
97   case TEXTURE_EXTERNAL_INDEX:
98      return WRITEMASK_XY;
99   case TEXTURE_3D_INDEX:
100   case TEXTURE_2D_ARRAY_INDEX:
101      return WRITEMASK_XYZ;
102   case TEXTURE_CUBE_INDEX:
103      return WRITEMASK_XYZ;
104   case TEXTURE_RECT_INDEX:
105      return WRITEMASK_XY;
106   default: return 0;
107   }
108}
109
110
111/* Step two: Basically this is dead code elimination.
112 *
113 * Iterate backwards over instructions, noting which values
114 * contribute to the final result.  Adjust writemasks to only
115 * calculate these values.
116 */
117void brw_wm_pass1( struct brw_wm_compile *c )
118{
119   GLint insn;
120
121   for (insn = c->nr_insns-1; insn >= 0; insn--) {
122      struct brw_wm_instruction *inst = &c->instruction[insn];
123      GLuint writemask;
124      GLuint read0, read1, read2;
125
126      if (inst->opcode == OPCODE_KIL) {
127	 track_arg(c, inst, 0, WRITEMASK_XYZW); /* All args contribute to final */
128	 continue;
129      }
130
131      if (inst->opcode == WM_FB_WRITE) {
132	 track_arg(c, inst, 0, WRITEMASK_XYZW);
133	 track_arg(c, inst, 1, WRITEMASK_XYZW);
134	 if (c->source_depth_to_render_target && c->computes_depth)
135	    track_arg(c, inst, 2, WRITEMASK_Z);
136	 else
137	    track_arg(c, inst, 2, 0);
138	 continue;
139      }
140
141      /* Lookup all the registers which were written by this
142       * instruction and get a mask of those that contribute to the output:
143       */
144      writemask = get_tracked_mask(c, inst);
145      if (!writemask) {
146	 GLuint arg;
147	 for (arg = 0; arg < 3; arg++)
148	    track_arg(c, inst, arg, 0);
149	 continue;
150      }
151
152      read0 = 0;
153      read1 = 0;
154      read2 = 0;
155
156      /* Mark all inputs which contribute to the marked outputs:
157       */
158      switch (inst->opcode) {
159      case OPCODE_ABS:
160      case OPCODE_FLR:
161      case OPCODE_FRC:
162      case OPCODE_MOV:
163      case OPCODE_SSG:
164      case OPCODE_SWZ:
165      case OPCODE_TRUNC:
166	 read0 = writemask;
167	 break;
168
169      case OPCODE_SUB:
170      case OPCODE_SLT:
171      case OPCODE_SLE:
172      case OPCODE_SGE:
173      case OPCODE_SGT:
174      case OPCODE_SEQ:
175      case OPCODE_SNE:
176      case OPCODE_ADD:
177      case OPCODE_MAX:
178      case OPCODE_MIN:
179      case OPCODE_MUL:
180	 read0 = writemask;
181	 read1 = writemask;
182	 break;
183
184      case OPCODE_DDX:
185      case OPCODE_DDY:
186	 read0 = writemask;
187	 break;
188
189      case OPCODE_MAD:
190      case OPCODE_CMP:
191      case OPCODE_LRP:
192	 read0 = writemask;
193	 read1 = writemask;
194	 read2 = writemask;
195	 break;
196
197      case OPCODE_XPD:
198	 if (writemask & WRITEMASK_X) read0 |= WRITEMASK_YZ;
199	 if (writemask & WRITEMASK_Y) read0 |= WRITEMASK_XZ;
200	 if (writemask & WRITEMASK_Z) read0 |= WRITEMASK_XY;
201	 read1 = read0;
202	 break;
203
204      case OPCODE_COS:
205      case OPCODE_EX2:
206      case OPCODE_LG2:
207      case OPCODE_RCP:
208      case OPCODE_RSQ:
209      case OPCODE_SIN:
210      case OPCODE_SCS:
211      case WM_CINTERP:
212      case WM_PIXELXY:
213	 read0 = WRITEMASK_X;
214	 break;
215
216      case OPCODE_POW:
217	 read0 = WRITEMASK_X;
218	 read1 = WRITEMASK_X;
219	 break;
220
221      case OPCODE_TEX:
222      case OPCODE_TXP:
223	 read0 = get_texcoord_mask(inst->tex_idx);
224
225         if (inst->tex_shadow)
226	    read0 |= WRITEMASK_Z;
227	 break;
228
229      case OPCODE_TXB:
230	 /* Shadow ignored for txb.
231	  */
232	 read0 = get_texcoord_mask(inst->tex_idx) | WRITEMASK_W;
233	 break;
234
235      case WM_WPOSXY:
236	 read0 = writemask & WRITEMASK_XY;
237	 break;
238
239      case WM_DELTAXY:
240	 read0 = writemask & WRITEMASK_XY;
241	 read1 = WRITEMASK_X;
242	 break;
243
244      case WM_PIXELW:
245	 read0 = WRITEMASK_X;
246	 read1 = WRITEMASK_XY;
247	 break;
248
249      case WM_LINTERP:
250	 read0 = WRITEMASK_X;
251	 read1 = WRITEMASK_XY;
252	 break;
253
254      case WM_PINTERP:
255	 read0 = WRITEMASK_X; /* interpolant */
256	 read1 = WRITEMASK_XY; /* deltas */
257	 read2 = WRITEMASK_W; /* pixel w */
258	 break;
259
260      case OPCODE_DP2:
261	 read0 = WRITEMASK_XY;
262	 read1 = WRITEMASK_XY;
263	 break;
264
265      case OPCODE_DP3:
266	 read0 = WRITEMASK_XYZ;
267	 read1 = WRITEMASK_XYZ;
268	 break;
269
270      case OPCODE_DPH:
271	 read0 = WRITEMASK_XYZ;
272	 read1 = WRITEMASK_XYZW;
273	 break;
274
275      case OPCODE_DP4:
276	 read0 = WRITEMASK_XYZW;
277	 read1 = WRITEMASK_XYZW;
278	 break;
279
280      case OPCODE_LIT:
281	 read0 = WRITEMASK_XYW;
282	 break;
283
284      case OPCODE_DST:
285      case WM_FRONTFACING:
286      default:
287	 break;
288      }
289
290      track_arg(c, inst, 0, read0);
291      track_arg(c, inst, 1, read1);
292      track_arg(c, inst, 2, read2);
293   }
294
295   if (unlikely(INTEL_DEBUG & DEBUG_WM)) {
296      brw_wm_print_program(c, "pass1");
297   }
298}
299