0f0de2134296053154bb7f5981fb0749f5bd79c5
[mesa.git] / src / gallium / drivers / r600 / sfn / sfn_instruction_tex.cpp
1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2019 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27 #include "sfn_instruction_tex.h"
28 #include "nir_builder.h"
29 #include "nir_builtin_builder.h"
30
31 namespace r600 {
32
33 TexInstruction::TexInstruction(Opcode op, const GPRVector &dest, const GPRVector &src,
34 unsigned sid, unsigned rid, PValue sampler_offset):
35 Instruction(tex),
36 m_opcode(op),
37 m_dst(dest),
38 m_src(src),
39 m_sampler_id(sid),
40 m_resource_id(rid),
41 m_flags(0),
42 m_inst_mode(0),
43 m_dest_swizzle{0,1,2,3},
44 m_sampler_offset(sampler_offset)
45
46 {
47 memset(m_offset, 0, sizeof (m_offset));
48
49 add_remappable_src_value(&m_src);
50 add_remappable_src_value(&m_sampler_offset);
51 add_remappable_dst_value(&m_dst);
52 }
53
54 void TexInstruction::set_gather_comp(int cmp)
55 {
56 m_inst_mode = cmp;
57 }
58
59 void TexInstruction::replace_values(const ValueSet& candiates, PValue new_value)
60 {
61 // I wonder whether we can actually end up here ...
62 for (auto c: candiates) {
63 if (*c == *m_src.reg_i(c->chan()))
64 m_src.set_reg_i(c->chan(), new_value);
65 if (*c == *m_dst.reg_i(c->chan()))
66 m_dst.set_reg_i(c->chan(), new_value);
67 }
68 }
69
70 void TexInstruction::set_offset(unsigned index, int32_t val)
71 {
72 assert(index < 3);
73 m_offset[index] = val;
74 }
75
76 int TexInstruction::get_offset(unsigned index) const
77 {
78 assert(index < 3);
79 return (m_offset[index] << 1 & 0x1f);
80 }
81
82 bool TexInstruction::is_equal_to(const Instruction& rhs) const
83 {
84 assert(rhs.type() == tex);
85 const auto& r = static_cast<const TexInstruction&>(rhs);
86 return (m_opcode == r.m_opcode &&
87 m_dst == r.m_dst &&
88 m_src == r.m_src &&
89 m_sampler_id == r.m_sampler_id &&
90 m_resource_id == r.m_resource_id);
91 }
92
93 void TexInstruction::do_print(std::ostream& os) const
94 {
95 const char *map_swz = "xyzw01?_";
96 os << opname(m_opcode) << " R" << m_dst.sel() << ".";
97 for (int i = 0; i < 4; ++i)
98 os << map_swz[m_dest_swizzle[i]];
99
100 os << " " << m_src
101 << " RESID:" << m_resource_id << " SAMPLER:"
102 << m_sampler_id;
103 }
104
105 const char *TexInstruction::opname(Opcode op)
106 {
107 switch (op) {
108 case ld: return "LD";
109 case get_resinfo: return "GET_TEXTURE_RESINFO";
110 case get_nsampled: return "GET_NUMBER_OF_SAMPLES";
111 case get_tex_lod: return "GET_LOD";
112 case get_gradient_h: return "GET_GRADIENTS_H";
113 case get_gradient_v: return "GET_GRADIENTS_V";
114 case set_offsets: return "SET_TEXTURE_OFFSETS";
115 case keep_gradients: return "KEEP_GRADIENTS";
116 case set_gradient_h: return "SET_GRADIENTS_H";
117 case set_gradient_v: return "SET_GRADIENTS_V";
118 case sample: return "SAMPLE";
119 case sample_l: return "SAMPLE_L";
120 case sample_lb: return "SAMPLE_LB";
121 case sample_lz: return "SAMPLE_LZ";
122 case sample_g: return "SAMPLE_G";
123 case sample_g_lb: return "SAMPLE_G_L";
124 case gather4: return "GATHER4";
125 case gather4_o: return "GATHER4_O";
126 case sample_c: return "SAMPLE_C";
127 case sample_c_l: return "SAMPLE_C_L";
128 case sample_c_lb: return "SAMPLE_C_LB";
129 case sample_c_lz: return "SAMPLE_C_LZ";
130 case sample_c_g: return "SAMPLE_C_G";
131 case sample_c_g_lb: return "SAMPLE_C_G_L";
132 case gather4_c: return "GATHER4_C";
133 case gather4_c_o: return "OP_GATHER4_C_O";
134 }
135 return "ERROR";
136 }
137
138
139
140 static bool lower_coord_shift_normalized(nir_builder& b, nir_tex_instr *tex)
141 {
142 b.cursor = nir_before_instr(&tex->instr);
143
144 nir_ssa_def * size = nir_i2f32(&b, nir_get_texture_size(&b, tex));
145 nir_ssa_def *scale = nir_frcp(&b, size);
146
147 int coord_index = nir_tex_instr_src_index(tex, nir_tex_src_coord);
148 nir_ssa_def *corr = nir_fadd(&b,
149 nir_fmul(&b, nir_imm_float(&b, -0.5f), scale),
150 tex->src[coord_index].src.ssa);
151 nir_instr_rewrite_src(&tex->instr, &tex->src[coord_index].src,
152 nir_src_for_ssa(corr));
153 return true;
154 }
155
156 static bool lower_coord_shift_unnormalized(nir_builder& b, nir_tex_instr *tex)
157 {
158 b.cursor = nir_before_instr(&tex->instr);
159 int coord_index = nir_tex_instr_src_index(tex, nir_tex_src_coord);
160 nir_ssa_def *corr = nir_fadd(&b, tex->src[coord_index].src.ssa,
161 nir_imm_float(&b, -0.5f));
162 nir_instr_rewrite_src(&tex->instr, &tex->src[coord_index].src,
163 nir_src_for_ssa(corr));
164 return true;
165 }
166
167 static bool
168 r600_nir_lower_int_tg4_impl(nir_function_impl *impl, const std::vector<bool>& lower)
169 {
170 nir_builder b;
171 nir_builder_init(&b, impl);
172
173 bool progress = false;
174 nir_foreach_block(block, impl) {
175 nir_foreach_instr_safe(instr, block) {
176 if (instr->type == nir_instr_type_tex) {
177 nir_tex_instr *tex = nir_instr_as_tex(instr);
178 if (tex->op == nir_texop_tg4 &&
179 tex->sampler_dim != GLSL_SAMPLER_DIM_CUBE) {
180 if (lower[tex->sampler_index]) {
181 if (tex->sampler_dim != GLSL_SAMPLER_DIM_RECT)
182 lower_coord_shift_normalized(b, tex);
183 else
184 lower_coord_shift_unnormalized(b, tex);
185 progress = true;
186 }
187 }
188 }
189 }
190 }
191 return progress;
192 }
193
194 /*
195 * This lowering pass works around a bug in r600 when doing TG4 from
196 * integral valued samplers.
197
198 * Gather4 should follow the same rules as bilinear filtering, but the hardware
199 * incorrectly forces nearest filtering if the texture format is integer.
200 * The only effect it has on Gather4, which always returns 4 texels for
201 * bilinear filtering, is that the final coordinates are off by 0.5 of
202 * the texel size.
203 */
204
205 bool r600_nir_lower_int_tg4(nir_shader *shader)
206 {
207 bool progress = false;
208 bool need_lowering = false;
209
210 int i = 0;
211
212 std::vector<bool> lower_sampler(shader->uniforms.length(), false);
213 auto is = lower_sampler.begin();
214
215 nir_foreach_variable(var, &shader->uniforms) {
216 if (var->type->is_sampler()) {
217 if (glsl_base_type_is_integer(var->type->sampled_type)) {
218 need_lowering = *is = true;
219 }
220 ++i;
221 ++is;
222 }
223 }
224
225 if (need_lowering) {
226 nir_foreach_function(function, shader) {
227 if (function->impl && r600_nir_lower_int_tg4_impl(function->impl, lower_sampler))
228 progress = true;
229 }
230 }
231
232 return progress;
233 }
234
235 static
236 bool lower_txl_txf_array_or_cube(nir_builder *b, nir_tex_instr *tex)
237 {
238 assert(tex->op == nir_texop_txb || tex->op == nir_texop_txl);
239 assert(nir_tex_instr_src_index(tex, nir_tex_src_ddx) < 0);
240 assert(nir_tex_instr_src_index(tex, nir_tex_src_ddy) < 0);
241
242 b->cursor = nir_before_instr(&tex->instr);
243
244 int lod_idx = nir_tex_instr_src_index(tex, nir_tex_src_lod);
245 int bias_idx = nir_tex_instr_src_index(tex, nir_tex_src_bias);
246 int min_lod_idx = nir_tex_instr_src_index(tex, nir_tex_src_min_lod);
247 assert (lod_idx >= 0 || bias_idx >= 0);
248
249 nir_ssa_def *size = nir_i2f32(b, nir_get_texture_size(b, tex));
250 nir_ssa_def *lod = (lod_idx >= 0) ?
251 nir_ssa_for_src(b, tex->src[lod_idx].src, 1) :
252 nir_get_texture_lod(b, tex);
253
254 if (bias_idx >= 0)
255 lod = nir_fadd(b, lod,nir_ssa_for_src(b, tex->src[bias_idx].src, 1));
256
257 if (min_lod_idx >= 0)
258 lod = nir_fmax(b, lod, nir_ssa_for_src(b, tex->src[min_lod_idx].src, 1));
259
260 /* max lod? */
261
262 nir_ssa_def *lambda_exp = nir_fexp2(b, lod);
263 nir_ssa_def *scale = NULL;
264
265 if (tex->is_array) {
266 int cmp_mask = (1 << (size->num_components - 1)) - 1;
267 scale = nir_frcp(b, nir_channels(b, size,
268 (nir_component_mask_t)cmp_mask));
269 } else if (tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE) {
270 unsigned int swizzle[NIR_MAX_VEC_COMPONENTS] = {0,0,0,0};
271 scale = nir_frcp(b, nir_channels(b, size, 1));
272 scale = nir_swizzle(b, scale, swizzle, 3);
273 }
274
275 nir_ssa_def *grad = nir_fmul(b, lambda_exp, scale);
276
277 if (lod_idx >= 0)
278 nir_tex_instr_remove_src(tex, lod_idx);
279 if (bias_idx >= 0)
280 nir_tex_instr_remove_src(tex, bias_idx);
281 if (min_lod_idx >= 0)
282 nir_tex_instr_remove_src(tex, min_lod_idx);
283 nir_tex_instr_add_src(tex, nir_tex_src_ddx, nir_src_for_ssa(grad));
284 nir_tex_instr_add_src(tex, nir_tex_src_ddy, nir_src_for_ssa(grad));
285
286 tex->op = nir_texop_txd;
287 return true;
288 }
289
290
291 static bool
292 r600_nir_lower_txl_txf_array_or_cube_impl(nir_function_impl *impl)
293 {
294 nir_builder b;
295 nir_builder_init(&b, impl);
296
297 bool progress = false;
298 nir_foreach_block(block, impl) {
299 nir_foreach_instr_safe(instr, block) {
300 if (instr->type == nir_instr_type_tex) {
301 nir_tex_instr *tex = nir_instr_as_tex(instr);
302
303 if (tex->is_shadow &&
304 (tex->op == nir_texop_txl || tex->op == nir_texop_txb) &&
305 (tex->is_array || tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE))
306 progress |= lower_txl_txf_array_or_cube(&b, tex);
307 }
308 }
309 }
310 return progress;
311 }
312
313 bool
314 r600_nir_lower_txl_txf_array_or_cube(nir_shader *shader)
315 {
316 bool progress = false;
317 nir_foreach_function(function, shader) {
318 if (function->impl && r600_nir_lower_txl_txf_array_or_cube_impl(function->impl))
319 progress = true;
320 }
321 return progress;
322 }
323
324
325 }