• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 Corbin Simpson
3  * Copyright 2010 Marek Olšák <maraeo@gmail.com>
4  * SPDX-License-Identifier: MIT
5  */
6 
7 #include "radeon_program_tex.h"
8 
9 #include "radeon_compiler_util.h"
10 
11 /* Series of transformations to be done on textures. */
12 
13 static void
scale_texcoords(struct r300_fragment_program_compiler * compiler,struct rc_instruction * inst,unsigned state_constant)14 scale_texcoords(struct r300_fragment_program_compiler *compiler, struct rc_instruction *inst,
15                 unsigned state_constant)
16 {
17    struct rc_instruction *inst_mov;
18 
19    unsigned temp = rc_find_free_temporary(&compiler->Base);
20 
21    inst_mov = rc_insert_new_instruction(&compiler->Base, inst->Prev);
22 
23    inst_mov->U.I.Opcode = RC_OPCODE_MUL;
24    inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
25    inst_mov->U.I.DstReg.Index = temp;
26    inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
27    inst_mov->U.I.SrcReg[1].File = RC_FILE_CONSTANT;
28    inst_mov->U.I.SrcReg[1].Index = rc_constants_add_state(&compiler->Base.Program.Constants,
29                                                           state_constant, inst->U.I.TexSrcUnit);
30 
31    reset_srcreg(&inst->U.I.SrcReg[0]);
32    inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
33    inst->U.I.SrcReg[0].Index = temp;
34 }
35 
36 static void
projective_divide(struct r300_fragment_program_compiler * compiler,struct rc_instruction * inst)37 projective_divide(struct r300_fragment_program_compiler *compiler, struct rc_instruction *inst)
38 {
39    struct rc_instruction *inst_mul, *inst_rcp;
40 
41    /* Make sure there is no temp reusing, some later passes depend on the SSA-like form. */
42    unsigned temp_rcp = rc_find_free_temporary(&compiler->Base);
43    unsigned temp_mul = rc_find_free_temporary(&compiler->Base);
44 
45    inst_rcp = rc_insert_new_instruction(&compiler->Base, inst->Prev);
46    inst_rcp->U.I.Opcode = RC_OPCODE_RCP;
47    inst_rcp->U.I.DstReg.File = RC_FILE_TEMPORARY;
48    inst_rcp->U.I.DstReg.Index = temp_rcp;
49    inst_rcp->U.I.DstReg.WriteMask = RC_MASK_W;
50    inst_rcp->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
51    /* Because the input can be arbitrarily swizzled,
52     * read the component mapped to W. */
53    inst_rcp->U.I.SrcReg[0].Swizzle = RC_MAKE_SWIZZLE_SMEAR(GET_SWZ(inst->U.I.SrcReg[0].Swizzle, 3));
54 
55    inst_mul = rc_insert_new_instruction(&compiler->Base, inst->Prev);
56    inst_mul->U.I.Opcode = RC_OPCODE_MUL;
57    inst_mul->U.I.DstReg.File = RC_FILE_TEMPORARY;
58    inst_mul->U.I.DstReg.Index = temp_mul;
59    inst_mul->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
60    inst_mul->U.I.SrcReg[1].File = RC_FILE_TEMPORARY;
61    inst_mul->U.I.SrcReg[1].Index = temp_rcp;
62    inst_mul->U.I.SrcReg[1].Swizzle = RC_SWIZZLE_WWWW;
63 
64    reset_srcreg(&inst->U.I.SrcReg[0]);
65    inst->U.I.Opcode = RC_OPCODE_TEX;
66    inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
67    inst->U.I.SrcReg[0].Index = temp_mul;
68 }
69 
70 /**
71  * Transform TEX, TXP, TXB, and KIL instructions in the following ways:
72  *  - implement texture compare (shadow extensions)
73  *  - extract non-native source / destination operands
74  *  - premultiply texture coordinates for RECT
75  *  - extract operand swizzles
76  *  - introduce a temporary register when write masks are needed
77  */
78 int
radeonTransformTEX(struct radeon_compiler * c,struct rc_instruction * inst,void * data)79 radeonTransformTEX(struct radeon_compiler *c, struct rc_instruction *inst, void *data)
80 {
81    struct r300_fragment_program_compiler *compiler = (struct r300_fragment_program_compiler *)data;
82    rc_wrap_mode wrapmode = compiler->state.unit[inst->U.I.TexSrcUnit].wrap_mode;
83    int is_rect = inst->U.I.TexSrcTarget == RC_TEXTURE_RECT;
84 
85    if (inst->U.I.Opcode != RC_OPCODE_TEX && inst->U.I.Opcode != RC_OPCODE_TXB &&
86        inst->U.I.Opcode != RC_OPCODE_TXP && inst->U.I.Opcode != RC_OPCODE_TXD &&
87        inst->U.I.Opcode != RC_OPCODE_TXL && inst->U.I.Opcode != RC_OPCODE_KIL)
88       return 0;
89 
90    /* R300 cannot sample from rectangles and the wrap mode fallback needs
91     * normalized coordinates anyway. */
92    if (inst->U.I.Opcode != RC_OPCODE_KIL && is_rect && (!c->is_r500 || wrapmode != RC_WRAP_NONE)) {
93       scale_texcoords(compiler, inst, RC_STATE_R300_TEXRECT_FACTOR);
94       inst->U.I.TexSrcTarget = RC_TEXTURE_2D;
95    }
96 
97    /* Divide by W if needed. */
98    if (inst->U.I.Opcode == RC_OPCODE_TXP &&
99        (wrapmode == RC_WRAP_REPEAT || wrapmode == RC_WRAP_MIRRORED_REPEAT ||
100         compiler->state.unit[inst->U.I.TexSrcUnit].clamp_and_scale_before_fetch)) {
101       projective_divide(compiler, inst);
102    }
103 
104    /* Texture wrap modes don't work on NPOT textures.
105     *
106     * Non-wrapped/clamped texcoords with NPOT are free in HW. Repeat and
107     * mirroring are not. If we need to repeat, we do:
108     *
109     * MUL temp, texcoord, <scaling factor constant>
110     * FRC temp, temp ; Discard integer portion of coords
111     *
112     * This gives us coords in [0, 1].
113     *
114     * Mirroring is trickier. We're going to start out like repeat:
115     *
116     * MUL temp, texcoord, <scaling factor constant> ; De-mirror across axes
117     * MUL temp, temp, 0.5 ; Pattern repeats in [0, 2]
118     *                            ; so scale to [0, 1]
119     * FRC temp, temp ; Make the pattern repeat
120     * MAD temp, temp, 2, -1 ; Move the pattern to [-1, 1]
121     * ADD temp, 1, -abs(temp) ; Now comes a neat trick: use abs to mirror the pattern.
122     *				; The pattern is backwards, so reverse it (1-x).
123     *
124     * This gives us coords in [0, 1].
125     *
126     * ~ C & M. ;)
127     */
128    if (inst->U.I.Opcode != RC_OPCODE_KIL && wrapmode != RC_WRAP_NONE) {
129       struct rc_instruction *inst_mov;
130       unsigned temp = rc_find_free_temporary(c);
131 
132       if (wrapmode == RC_WRAP_REPEAT) {
133          /* Both instructions will be paired up. */
134          struct rc_instruction *inst_frc = rc_insert_new_instruction(c, inst->Prev);
135 
136          inst_frc->U.I.Opcode = RC_OPCODE_FRC;
137          inst_frc->U.I.DstReg.File = RC_FILE_TEMPORARY;
138          inst_frc->U.I.DstReg.Index = temp;
139          inst_frc->U.I.DstReg.WriteMask = RC_MASK_XYZ;
140          inst_frc->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
141       } else if (wrapmode == RC_WRAP_MIRRORED_REPEAT) {
142          /*
143           * Function:
144           *   f(v) = 1 - abs(frac(v * 0.5) * 2 - 1)
145           *
146           * Code:
147           *   MUL temp, src0, 0.5
148           *   FRC temp, temp
149           *   MAD temp, temp, 2, -1
150           *   ADD temp, 1, -abs(temp)
151           */
152 
153          struct rc_instruction *inst_mul, *inst_frc, *inst_mad, *inst_add;
154          unsigned two, two_swizzle;
155 
156          inst_mul = rc_insert_new_instruction(c, inst->Prev);
157          unsigned temp_mul = rc_find_free_temporary(c);
158 
159          inst_mul->U.I.Opcode = RC_OPCODE_MUL;
160          inst_mul->U.I.DstReg.File = RC_FILE_TEMPORARY;
161          inst_mul->U.I.DstReg.Index = temp_mul;
162          inst_mul->U.I.DstReg.WriteMask = RC_MASK_XYZ;
163          inst_mul->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
164          inst_mul->U.I.SrcReg[1].Swizzle = RC_SWIZZLE_HHHH;
165 
166          inst_frc = rc_insert_new_instruction(c, inst->Prev);
167          unsigned temp_frc = rc_find_free_temporary(c);
168 
169          inst_frc->U.I.Opcode = RC_OPCODE_FRC;
170          inst_frc->U.I.DstReg.File = RC_FILE_TEMPORARY;
171          inst_frc->U.I.DstReg.Index = temp_frc;
172          inst_frc->U.I.DstReg.WriteMask = RC_MASK_XYZ;
173          inst_frc->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
174          inst_frc->U.I.SrcReg[0].Index = temp_mul;
175          inst_frc->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZ0;
176 
177          two = rc_constants_add_immediate_scalar(&c->Program.Constants, 2, &two_swizzle);
178          inst_mad = rc_insert_new_instruction(c, inst->Prev);
179          unsigned temp_mad = rc_find_free_temporary(c);
180 
181          inst_mad->U.I.Opcode = RC_OPCODE_MAD;
182          inst_mad->U.I.DstReg.File = RC_FILE_TEMPORARY;
183          inst_mad->U.I.DstReg.Index = temp_mad;
184          inst_mad->U.I.DstReg.WriteMask = RC_MASK_XYZ;
185          inst_mad->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
186          inst_mad->U.I.SrcReg[0].Index = temp_frc;
187          inst_mad->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_XYZ0;
188          inst_mad->U.I.SrcReg[1].File = RC_FILE_CONSTANT;
189          inst_mad->U.I.SrcReg[1].Index = two;
190          inst_mad->U.I.SrcReg[1].Swizzle = two_swizzle;
191          inst_mad->U.I.SrcReg[2].Swizzle = RC_SWIZZLE_1111;
192          inst_mad->U.I.SrcReg[2].Negate = RC_MASK_XYZ;
193 
194          inst_add = rc_insert_new_instruction(c, inst->Prev);
195 
196          inst_add->U.I.Opcode = RC_OPCODE_ADD;
197          inst_add->U.I.DstReg.File = RC_FILE_TEMPORARY;
198          inst_add->U.I.DstReg.Index = temp;
199          inst_add->U.I.DstReg.WriteMask = RC_MASK_XYZ;
200          inst_add->U.I.SrcReg[0].Swizzle = RC_SWIZZLE_1111;
201          inst_add->U.I.SrcReg[1].File = RC_FILE_TEMPORARY;
202          inst_add->U.I.SrcReg[1].Index = temp_mad;
203          inst_add->U.I.SrcReg[1].Swizzle = RC_SWIZZLE_XYZ0;
204          inst_add->U.I.SrcReg[1].Abs = 1;
205          inst_add->U.I.SrcReg[1].Negate = RC_MASK_XYZ;
206       } else if (wrapmode == RC_WRAP_MIRRORED_CLAMP) {
207          /*
208           * Mirrored clamp modes are bloody simple, we just use abs
209           * to mirror [0, 1] into [-1, 0]. This works for
210           * all modes i.e. CLAMP, CLAMP_TO_EDGE, and CLAMP_TO_BORDER.
211           */
212          struct rc_instruction *inst_mov;
213 
214          inst_mov = rc_insert_new_instruction(c, inst->Prev);
215 
216          inst_mov->U.I.Opcode = RC_OPCODE_MOV;
217          inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
218          inst_mov->U.I.DstReg.Index = temp;
219          inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZ;
220          inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
221          inst_mov->U.I.SrcReg[0].Abs = 1;
222       }
223 
224       /* Preserve W for TXP/TXB. */
225       inst_mov = rc_insert_new_instruction(c, inst->Prev);
226 
227       inst_mov->U.I.Opcode = RC_OPCODE_MOV;
228       inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
229       inst_mov->U.I.DstReg.Index = temp;
230       inst_mov->U.I.DstReg.WriteMask = RC_MASK_W;
231       inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
232 
233       reset_srcreg(&inst->U.I.SrcReg[0]);
234       inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
235       inst->U.I.SrcReg[0].Index = temp;
236    }
237 
238    /* NPOT -> POT conversion for 3D textures. */
239    if (inst->U.I.Opcode != RC_OPCODE_KIL &&
240        compiler->state.unit[inst->U.I.TexSrcUnit].clamp_and_scale_before_fetch) {
241       struct rc_instruction *inst_mov;
242       unsigned temp = rc_find_free_temporary(c);
243 
244       /* Saturate XYZ. */
245       inst_mov = rc_insert_new_instruction(c, inst->Prev);
246       inst_mov->U.I.Opcode = RC_OPCODE_MOV;
247       inst_mov->U.I.SaturateMode = RC_SATURATE_ZERO_ONE;
248       inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
249       inst_mov->U.I.DstReg.Index = temp;
250       inst_mov->U.I.DstReg.WriteMask = RC_MASK_XYZ;
251       inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
252 
253       /* Copy W. */
254       inst_mov = rc_insert_new_instruction(c, inst->Prev);
255       inst_mov->U.I.Opcode = RC_OPCODE_MOV;
256       inst_mov->U.I.DstReg.File = RC_FILE_TEMPORARY;
257       inst_mov->U.I.DstReg.Index = temp;
258       inst_mov->U.I.DstReg.WriteMask = RC_MASK_W;
259       inst_mov->U.I.SrcReg[0] = inst->U.I.SrcReg[0];
260 
261       reset_srcreg(&inst->U.I.SrcReg[0]);
262       inst->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
263       inst->U.I.SrcReg[0].Index = temp;
264 
265       scale_texcoords(compiler, inst, RC_STATE_R300_TEXSCALE_FACTOR);
266    }
267 
268    /* Cannot write texture to output registers or with saturate (all chips),
269     * or with masks (non-r500). */
270    if (inst->U.I.Opcode != RC_OPCODE_KIL) {
271       /* We should not be getting saturates on TEX, but assert just to be sure. */
272       assert(!inst->U.I.SaturateMode);
273 
274       if (inst->U.I.DstReg.File != RC_FILE_TEMPORARY || inst->U.I.SaturateMode ||
275           (!c->is_r500 && inst->U.I.DstReg.WriteMask != RC_MASK_XYZW)) {
276          struct rc_instruction *inst_mov = rc_insert_new_instruction(c, inst);
277 
278          inst_mov->U.I.Opcode = RC_OPCODE_MOV;
279          inst_mov->U.I.SaturateMode = inst->U.I.SaturateMode;
280          inst_mov->U.I.DstReg = inst->U.I.DstReg;
281          inst_mov->U.I.SrcReg[0].File = RC_FILE_TEMPORARY;
282          inst_mov->U.I.SrcReg[0].Index = rc_find_free_temporary(c);
283 
284          inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
285          inst->U.I.DstReg.Index = inst_mov->U.I.SrcReg[0].Index;
286          inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
287       }
288    }
289 
290    return 1;
291 }
292