1 /*
2 * Copyright 2009 Nicolai Hähnle <nhaehnle@gmail.com>
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "radeon_compiler.h"
7
8 #include <stdio.h>
9
10 #include "r300_fragprog.h"
11 #include "r300_fragprog_swizzle.h"
12 #include "r500_fragprog.h"
13 #include "radeon_compiler_util.h"
14 #include "radeon_dataflow.h"
15 #include "radeon_list.h"
16 #include "radeon_program_alu.h"
17 #include "radeon_program_tex.h"
18 #include "radeon_remove_constants.h"
19 #include "radeon_variable.h"
20
21 static void
rc_rewrite_depth_out(struct radeon_compiler * cc,void * user)22 rc_rewrite_depth_out(struct radeon_compiler *cc, void *user)
23 {
24 struct r300_fragment_program_compiler *c = (struct r300_fragment_program_compiler *)cc;
25 struct rc_instruction *rci;
26
27 for (rci = c->Base.Program.Instructions.Next; rci != &c->Base.Program.Instructions;
28 rci = rci->Next) {
29 struct rc_sub_instruction *inst = &rci->U.I;
30 unsigned i;
31 const struct rc_opcode_info *info = rc_get_opcode_info(inst->Opcode);
32
33 if (inst->DstReg.File != RC_FILE_OUTPUT || inst->DstReg.Index != c->OutputDepth)
34 continue;
35
36 if (inst->DstReg.WriteMask & RC_MASK_Z) {
37 inst->DstReg.WriteMask = RC_MASK_W;
38 } else {
39 inst->DstReg.WriteMask = 0;
40 continue;
41 }
42
43 if (!info->IsComponentwise) {
44 continue;
45 }
46
47 for (i = 0; i < info->NumSrcRegs; i++) {
48 inst->SrcReg[i] = lmul_swizzle(RC_SWIZZLE_ZZZZ, inst->SrcReg[i]);
49 }
50 }
51 }
52
53 /**
54 * This function will try to convert rgb instructions into alpha instructions
55 * and vice versa. While this is already attempted during the pair scheduling,
56 * it is much simpler to do it before pair conversion, so do it here at least for
57 * the simple cases.
58 *
59 * Currently only math opcodes writing to rgb (and with no friends) are
60 * converted to alpha.
61 *
62 * This function assumes all the instructions are still of type
63 * RC_INSTRUCTION_NORMAL, the conversion is much simpler.
64 *
65 * Beware that this needs to be also called before doing presubtract, because
66 * rc_get_variables can't get properly readers for normal instructions if presubtract
67 * is present (it works fine for pair instructions).
68 */
69 static void
rc_convert_rgb_alpha(struct radeon_compiler * c,void * user)70 rc_convert_rgb_alpha(struct radeon_compiler *c, void *user)
71 {
72 struct rc_list *variables;
73 struct rc_list *var_ptr;
74
75 variables = rc_get_variables(c);
76
77 for (var_ptr = variables; var_ptr; var_ptr = var_ptr->Next) {
78 struct rc_variable *var = var_ptr->Item;
79
80 if (var->Inst->U.I.DstReg.File != RC_FILE_TEMPORARY) {
81 continue;
82 }
83
84 /* Only rewrite scalar opcodes that are used separately for now. */
85 if (var->Friend)
86 continue;
87
88 const struct rc_opcode_info *opcode = rc_get_opcode_info(var->Inst->U.I.Opcode);
89 if (opcode->IsStandardScalar && var->Dst.WriteMask != RC_MASK_W) {
90 unsigned index = rc_find_free_temporary(c);
91 rc_variable_change_dst(var, index, RC_MASK_W);
92 }
93
94 /* Here we attempt to convert some code specific for the shadow lowering to use the W
95 * channel. Most notably this prevents some unfavorable presubtract later.
96 *
97 * TODO: This should not be needed once we can properly vectorize the reference value
98 * comparisons.
99 */
100 if (var->Inst->U.I.Opcode == RC_OPCODE_ADD &&
101 var->Inst->U.I.SrcReg[0].File == RC_FILE_TEMPORARY &&
102 var->Inst->U.I.SrcReg[1].File == RC_FILE_TEMPORARY &&
103 var->Inst->U.I.DstReg.File == RC_FILE_TEMPORARY &&
104 var->Inst->U.I.DstReg.WriteMask == RC_MASK_X) {
105 unsigned have_tex = false;
106 struct rc_variable *fsat = NULL;
107 for (unsigned int src = 0; src < 2; src++) {
108 struct rc_list *writer_list;
109 writer_list = rc_variable_list_get_writers(variables, RC_INSTRUCTION_NORMAL,
110 &var->Inst->U.I.SrcReg[src]);
111 if (!writer_list || !writer_list->Item)
112 continue;
113
114 struct rc_variable *src_variable = (struct rc_variable *)writer_list->Item;
115 struct rc_instruction *inst = src_variable->Inst;
116 const struct rc_opcode_info *info = rc_get_opcode_info(inst->U.I.Opcode);
117
118 /* Here we check that the two sources are the depth texture and saturated MOV/MUL */
119 if (info->HasTexture && inst->U.I.DstReg.WriteMask == RC_MASK_X && !have_tex && !src_variable->Friend) {
120 have_tex = true;
121 }
122 if ((inst->U.I.Opcode == RC_OPCODE_MOV || inst->U.I.Opcode == RC_OPCODE_ADD) && !fsat &&
123 inst->U.I.SaturateMode != RC_SATURATE_NONE && inst->U.I.DstReg.WriteMask == RC_MASK_X &&
124 !src_variable->Friend) {
125 fsat = src_variable;
126 }
127 }
128
129 /* Move the calculations to W. */
130 if (fsat && have_tex) {
131 unsigned index = rc_find_free_temporary(c);
132 rc_variable_change_dst(var, index, RC_MASK_W);
133 index = rc_find_free_temporary(c);
134 rc_variable_change_dst(fsat, index, RC_MASK_W);
135 }
136 }
137 }
138 }
139
140 void
r3xx_compile_fragment_program(struct r300_fragment_program_compiler * c)141 r3xx_compile_fragment_program(struct r300_fragment_program_compiler *c)
142 {
143 int is_r500 = c->Base.is_r500;
144 int opt = !c->Base.disable_optimizations;
145 int alpha2one = c->state.alpha_to_one;
146 bool dbg = c->Base.Debug & RC_DBG_LOG;
147
148 /* Lists of instruction transformations. */
149 struct radeon_program_transformation force_alpha_to_one[] = {{&rc_force_output_alpha_to_one, c},
150 {NULL, NULL}};
151
152 struct radeon_program_transformation rewrite_tex[] = {{&radeonTransformTEX, c}, {NULL, NULL}};
153
154 struct radeon_program_transformation native_rewrite_r500[] = {{&radeonTransformALU, NULL},
155 {&radeonTransformDeriv, NULL},
156 {NULL, NULL}};
157
158 struct radeon_program_transformation native_rewrite_r300[] = {{&radeonTransformALU, NULL},
159 {&radeonStubDeriv, NULL},
160 {NULL, NULL}};
161
162 struct radeon_program_transformation opt_presubtract[] = {{&rc_opt_presubtract, NULL},
163 {NULL, NULL}};
164
165 /* List of compiler passes. */
166 /* clang-format off */
167 struct radeon_compiler_pass fs_list[] = {
168 /* NAME DUMP PREDICATE FUNCTION PARAM */
169 {"rewrite depth out", 1, 1, rc_rewrite_depth_out, NULL},
170 {"force alpha to one", 1, alpha2one, rc_local_transform, force_alpha_to_one},
171 {"transform TEX", 1, 1, rc_local_transform, rewrite_tex},
172 {"transform IF", 1, is_r500, r500_transform_IF, NULL},
173 {"native rewrite", 1, is_r500, rc_local_transform, native_rewrite_r500},
174 {"native rewrite", 1, !is_r500, rc_local_transform, native_rewrite_r300},
175 {"deadcode", 1, opt, rc_dataflow_deadcode, NULL},
176 {"convert rgb<->alpha", 1, opt, rc_convert_rgb_alpha, NULL},
177 {"dataflow optimize", 1, opt, rc_optimize, NULL},
178 {"inline literals", 1, is_r500 && opt, rc_inline_literals, NULL},
179 {"dataflow swizzles", 1, 1, rc_dataflow_swizzles, NULL},
180 {"dead constants", 1, 1, rc_remove_unused_constants, &c->code->constants_remap_table},
181 {"dataflow presubtract", 1, opt, rc_local_transform, opt_presubtract},
182 {"pair translate", 1, 1, rc_pair_translate, NULL},
183 {"pair scheduling", 1, 1, rc_pair_schedule, &opt},
184 {"dead sources", 1, 1, rc_pair_remove_dead_sources, NULL},
185 {"register allocation", 1, 1, rc_pair_regalloc, &opt},
186 {"final code validation", 0, 1, rc_validate_final_shader, NULL},
187 {"machine code generation", 0, is_r500, r500BuildFragmentProgramHwCode, NULL},
188 {"machine code generation", 0, !is_r500, r300BuildFragmentProgramHwCode, NULL},
189 {"dump machine code", 0, is_r500 && dbg, r500FragmentProgramDump, NULL},
190 {"dump machine code", 0, !is_r500 && dbg, r300FragmentProgramDump, NULL},
191 {NULL, 0, 0, NULL, NULL}};
192 /* clang-format on */
193
194 c->Base.type = RC_FRAGMENT_PROGRAM;
195 c->Base.SwizzleCaps = c->Base.is_r500 ? &r500_swizzle_caps : &r300_swizzle_caps;
196
197 rc_run_compiler(&c->Base, fs_list);
198
199 rc_constants_copy(&c->code->constants, &c->Base.Program.Constants);
200 }
201