1 /*
2 * Copyright © 2023 Valve Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "nir/nir.h"
25 #include "nir/nir_builder.h"
26 #include "radv_nir.h"
27 #include "radv_private.h"
28
29 typedef struct {
30 bool dynamic_rasterization_samples;
31 unsigned num_rasterization_samples;
32 unsigned rast_prim;
33 } lower_fs_barycentric_state;
34
35 static nir_def *
lower_interp_center_smooth(nir_builder * b,nir_def * offset)36 lower_interp_center_smooth(nir_builder *b, nir_def *offset)
37 {
38 nir_def *pull_model = nir_load_barycentric_model(b, 32);
39
40 nir_def *deriv_x =
41 nir_vec3(b, nir_fddx_fine(b, nir_channel(b, pull_model, 0)), nir_fddx_fine(b, nir_channel(b, pull_model, 1)),
42 nir_fddx_fine(b, nir_channel(b, pull_model, 2)));
43 nir_def *deriv_y =
44 nir_vec3(b, nir_fddy_fine(b, nir_channel(b, pull_model, 0)), nir_fddy_fine(b, nir_channel(b, pull_model, 1)),
45 nir_fddy_fine(b, nir_channel(b, pull_model, 2)));
46
47 nir_def *offset_x = nir_channel(b, offset, 0);
48 nir_def *offset_y = nir_channel(b, offset, 1);
49
50 nir_def *adjusted_x = nir_fadd(b, pull_model, nir_fmul(b, deriv_x, offset_x));
51 nir_def *adjusted = nir_fadd(b, adjusted_x, nir_fmul(b, deriv_y, offset_y));
52
53 nir_def *ij = nir_vec2(b, nir_channel(b, adjusted, 0), nir_channel(b, adjusted, 1));
54
55 /* Get W by using the reciprocal of 1/W. */
56 nir_def *w = nir_frcp(b, nir_channel(b, adjusted, 2));
57
58 return nir_fmul(b, ij, w);
59 }
60
61 static nir_def *
lower_barycentric_coord_at_offset(nir_builder * b,nir_def * src,enum glsl_interp_mode mode)62 lower_barycentric_coord_at_offset(nir_builder *b, nir_def *src, enum glsl_interp_mode mode)
63 {
64 if (mode == INTERP_MODE_SMOOTH)
65 return lower_interp_center_smooth(b, src);
66
67 return nir_load_barycentric_at_offset(b, 32, src, .interp_mode = mode);
68 }
69
70 static nir_def *
lower_barycentric_coord_at_sample(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)71 lower_barycentric_coord_at_sample(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
72 {
73 const enum glsl_interp_mode mode = (enum glsl_interp_mode)nir_intrinsic_interp_mode(intrin);
74 nir_def *num_samples = nir_load_rasterization_samples_amd(b);
75 nir_def *new_dest;
76
77 if (state->dynamic_rasterization_samples) {
78 nir_def *res1, *res2;
79
80 nir_push_if(b, nir_ieq_imm(b, num_samples, 1));
81 {
82 res1 = nir_load_barycentric_pixel(b, 32, .interp_mode = nir_intrinsic_interp_mode(intrin));
83 }
84 nir_push_else(b, NULL);
85 {
86 nir_def *sample_pos = nir_load_sample_positions_amd(b, 32, intrin->src[0].ssa, num_samples);
87
88 /* sample_pos -= 0.5 */
89 sample_pos = nir_fadd_imm(b, sample_pos, -0.5f);
90
91 res2 = lower_barycentric_coord_at_offset(b, sample_pos, mode);
92 }
93 nir_pop_if(b, NULL);
94
95 new_dest = nir_if_phi(b, res1, res2);
96 } else {
97 if (!state->num_rasterization_samples) {
98 new_dest = nir_load_barycentric_pixel(b, 32, .interp_mode = nir_intrinsic_interp_mode(intrin));
99 } else {
100 nir_def *sample_pos = nir_load_sample_positions_amd(b, 32, intrin->src[0].ssa, num_samples);
101
102 /* sample_pos -= 0.5 */
103 sample_pos = nir_fadd_imm(b, sample_pos, -0.5f);
104
105 new_dest = lower_barycentric_coord_at_offset(b, sample_pos, mode);
106 }
107 }
108
109 return new_dest;
110 }
111
112 static nir_def *
get_interp_param(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)113 get_interp_param(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
114 {
115 const enum glsl_interp_mode mode = (enum glsl_interp_mode)nir_intrinsic_interp_mode(intrin);
116
117 if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_pixel) {
118 return nir_load_barycentric_pixel(b, 32, .interp_mode = mode);
119 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_at_offset) {
120 return lower_barycentric_coord_at_offset(b, intrin->src[0].ssa, mode);
121 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_at_sample) {
122 return lower_barycentric_coord_at_sample(b, state, intrin);
123 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_centroid) {
124 return nir_load_barycentric_centroid(b, 32, .interp_mode = mode);
125 } else {
126 assert(intrin->intrinsic == nir_intrinsic_load_barycentric_coord_sample);
127 return nir_load_barycentric_sample(b, 32, .interp_mode = mode);
128 }
129
130 return NULL;
131 }
132
133 static nir_def *
lower_point(nir_builder * b)134 lower_point(nir_builder *b)
135 {
136 nir_def *coords[3];
137
138 coords[0] = nir_imm_float(b, 1.0f);
139 coords[1] = nir_imm_float(b, 0.0f);
140 coords[2] = nir_imm_float(b, 0.0f);
141
142 return nir_vec(b, coords, 3);
143 }
144
145 static nir_def *
lower_line(nir_builder * b,nir_def * p1,nir_def * p2)146 lower_line(nir_builder *b, nir_def *p1, nir_def *p2)
147 {
148 nir_def *coords[3];
149
150 coords[1] = nir_fadd(b, p1, p2);
151 coords[0] = nir_fsub_imm(b, 1.0f, coords[1]);
152 coords[2] = nir_imm_float(b, 0.0f);
153
154 return nir_vec(b, coords, 3);
155 }
156
157 static nir_def *
lower_triangle(nir_builder * b,nir_def * p1,nir_def * p2)158 lower_triangle(nir_builder *b, nir_def *p1, nir_def *p2)
159 {
160 nir_def *v0_bary[3], *v1_bary[3], *v2_bary[3];
161 nir_def *coords[3];
162
163 /* Compute the provoking vertex ID:
164 *
165 * quad_id = thread_id >> 2
166 * provoking_vtx_id = (provoking_vtx >> (quad_id << 1)) & 3
167 */
168 nir_def *quad_id = nir_ushr_imm(b, nir_load_subgroup_invocation(b), 2);
169 nir_def *provoking_vtx = nir_load_provoking_vtx_amd(b);
170 nir_def *provoking_vtx_id = nir_ubfe(b, provoking_vtx, nir_ishl_imm(b, quad_id, 1), nir_imm_int(b, 2));
171
172 /* Compute barycentrics. */
173 v0_bary[0] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
174 v0_bary[1] = p1;
175 v0_bary[2] = p2;
176
177 v1_bary[0] = p1;
178 v1_bary[1] = p2;
179 v1_bary[2] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
180
181 v2_bary[0] = p2;
182 v2_bary[1] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
183 v2_bary[2] = p1;
184
185 /* Select barycentrics for the given provoking vertex ID. */
186 for (unsigned i = 0; i < 3; i++) {
187 coords[i] = nir_bcsel(b, nir_ieq_imm(b, provoking_vtx_id, 2), v2_bary[i],
188 nir_bcsel(b, nir_ieq_imm(b, provoking_vtx_id, 1), v1_bary[i], v0_bary[i]));
189 }
190
191 return nir_vec(b, coords, 3);
192 }
193
194 static bool
lower_load_barycentric_coord(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)195 lower_load_barycentric_coord(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
196 {
197 nir_def *interp, *p1, *p2;
198 nir_def *new_dest;
199
200 b->cursor = nir_after_instr(&intrin->instr);
201
202 /* When the rasterization primitive isn't known at compile time (GPL), load it. */
203 if (state->rast_prim == -1) {
204 nir_def *rast_prim = nir_load_rasterization_primitive_amd(b);
205 nir_def *res1, *res2;
206
207 nir_def *is_point = nir_ieq_imm(b, rast_prim, V_028A6C_POINTLIST);
208 nir_if *if_point = nir_push_if(b, is_point);
209 {
210 res1 = lower_point(b);
211 }
212 nir_push_else(b, if_point);
213 {
214 nir_def *res_line, *res_triangle;
215
216 interp = get_interp_param(b, state, intrin);
217 p1 = nir_channel(b, interp, 0);
218 p2 = nir_channel(b, interp, 1);
219
220 nir_def *is_line = nir_ieq_imm(b, rast_prim, V_028A6C_LINESTRIP);
221 nir_if *if_line = nir_push_if(b, is_line);
222 {
223 res_line = lower_line(b, p1, p2);
224 }
225 nir_push_else(b, if_line);
226 {
227 res_triangle = lower_triangle(b, p1, p2);
228 }
229 nir_pop_if(b, if_line);
230
231 res2 = nir_if_phi(b, res_line, res_triangle);
232 }
233 nir_pop_if(b, if_point);
234
235 new_dest = nir_if_phi(b, res1, res2);
236 } else {
237 if (state->rast_prim == V_028A6C_POINTLIST) {
238 new_dest = lower_point(b);
239 } else {
240 interp = get_interp_param(b, state, intrin);
241 p1 = nir_channel(b, interp, 0);
242 p2 = nir_channel(b, interp, 1);
243
244 if (state->rast_prim == V_028A6C_LINESTRIP) {
245 new_dest = lower_line(b, p1, p2);
246 } else {
247 assert(state->rast_prim == V_028A6C_TRISTRIP);
248 new_dest = lower_triangle(b, p1, p2);
249 }
250 }
251 }
252
253 nir_def_rewrite_uses(&intrin->def, new_dest);
254 nir_instr_remove(&intrin->instr);
255
256 return true;
257 }
258
259 bool
radv_nir_lower_fs_barycentric(nir_shader * shader,const struct radv_graphics_state_key * gfx_state,unsigned rast_prim)260 radv_nir_lower_fs_barycentric(nir_shader *shader, const struct radv_graphics_state_key *gfx_state, unsigned rast_prim)
261 {
262 nir_function_impl *impl = nir_shader_get_entrypoint(shader);
263 bool progress = false;
264
265 nir_builder b;
266
267 lower_fs_barycentric_state state = {
268 .dynamic_rasterization_samples = gfx_state->dynamic_rasterization_samples,
269 .num_rasterization_samples = gfx_state->ms.rasterization_samples,
270 .rast_prim = rast_prim,
271 };
272
273 nir_foreach_function (function, shader) {
274 if (!function->impl)
275 continue;
276
277 b = nir_builder_create(function->impl);
278
279 nir_foreach_block (block, impl) {
280 nir_foreach_instr_safe (instr, block) {
281 if (instr->type != nir_instr_type_intrinsic)
282 continue;
283
284 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
285 if (intrin->intrinsic != nir_intrinsic_load_barycentric_coord_pixel &&
286 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_centroid &&
287 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_sample &&
288 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_at_offset &&
289 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_at_sample)
290 continue;
291
292 progress |= lower_load_barycentric_coord(&b, &state, intrin);
293 }
294 }
295 }
296
297 nir_metadata_preserve(impl, progress ? nir_metadata_none : nir_metadata_all);
298
299 return progress;
300 }
301