1 /*
2 * Copyright © 2023 Valve Corporation
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include "nir/nir.h"
8 #include "nir/nir_builder.h"
9 #include "radv_nir.h"
10 #include "radv_pipeline_graphics.h"
11 #include "sid.h"
12
13 typedef struct {
14 bool dynamic_rasterization_samples;
15 unsigned num_rasterization_samples;
16 unsigned rast_prim;
17 } lower_fs_barycentric_state;
18
19 static nir_def *
lower_interp_center_smooth(nir_builder * b,nir_def * offset)20 lower_interp_center_smooth(nir_builder *b, nir_def *offset)
21 {
22 nir_def *pull_model = nir_load_barycentric_model(b, 32);
23
24 nir_def *deriv_x =
25 nir_vec3(b, nir_ddx_fine(b, nir_channel(b, pull_model, 0)), nir_ddx_fine(b, nir_channel(b, pull_model, 1)),
26 nir_ddx_fine(b, nir_channel(b, pull_model, 2)));
27 nir_def *deriv_y =
28 nir_vec3(b, nir_ddy_fine(b, nir_channel(b, pull_model, 0)), nir_ddy_fine(b, nir_channel(b, pull_model, 1)),
29 nir_ddy_fine(b, nir_channel(b, pull_model, 2)));
30
31 nir_def *offset_x = nir_channel(b, offset, 0);
32 nir_def *offset_y = nir_channel(b, offset, 1);
33
34 nir_def *adjusted_x = nir_fadd(b, pull_model, nir_fmul(b, deriv_x, offset_x));
35 nir_def *adjusted = nir_fadd(b, adjusted_x, nir_fmul(b, deriv_y, offset_y));
36
37 nir_def *ij = nir_vec2(b, nir_channel(b, adjusted, 0), nir_channel(b, adjusted, 1));
38
39 /* Get W by using the reciprocal of 1/W. */
40 nir_def *w = nir_frcp(b, nir_channel(b, adjusted, 2));
41
42 return nir_fmul(b, ij, w);
43 }
44
45 static nir_def *
lower_barycentric_coord_at_offset(nir_builder * b,nir_def * src,enum glsl_interp_mode mode)46 lower_barycentric_coord_at_offset(nir_builder *b, nir_def *src, enum glsl_interp_mode mode)
47 {
48 if (mode == INTERP_MODE_SMOOTH)
49 return lower_interp_center_smooth(b, src);
50
51 return nir_load_barycentric_at_offset(b, 32, src, .interp_mode = mode);
52 }
53
54 static nir_def *
lower_barycentric_coord_at_sample(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)55 lower_barycentric_coord_at_sample(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
56 {
57 const enum glsl_interp_mode mode = (enum glsl_interp_mode)nir_intrinsic_interp_mode(intrin);
58 nir_def *num_samples = nir_load_rasterization_samples_amd(b);
59 nir_def *new_dest;
60
61 if (state->dynamic_rasterization_samples) {
62 nir_def *res1, *res2;
63
64 nir_push_if(b, nir_ieq_imm(b, num_samples, 1));
65 {
66 res1 = nir_load_barycentric_pixel(b, 32, .interp_mode = nir_intrinsic_interp_mode(intrin));
67 }
68 nir_push_else(b, NULL);
69 {
70 nir_def *sample_pos = nir_load_sample_positions_amd(b, 32, intrin->src[0].ssa, num_samples);
71
72 /* sample_pos -= 0.5 */
73 sample_pos = nir_fadd_imm(b, sample_pos, -0.5f);
74
75 res2 = lower_barycentric_coord_at_offset(b, sample_pos, mode);
76 }
77 nir_pop_if(b, NULL);
78
79 new_dest = nir_if_phi(b, res1, res2);
80 } else {
81 if (!state->num_rasterization_samples) {
82 new_dest = nir_load_barycentric_pixel(b, 32, .interp_mode = nir_intrinsic_interp_mode(intrin));
83 } else {
84 nir_def *sample_pos = nir_load_sample_positions_amd(b, 32, intrin->src[0].ssa, num_samples);
85
86 /* sample_pos -= 0.5 */
87 sample_pos = nir_fadd_imm(b, sample_pos, -0.5f);
88
89 new_dest = lower_barycentric_coord_at_offset(b, sample_pos, mode);
90 }
91 }
92
93 return new_dest;
94 }
95
96 static nir_def *
get_interp_param(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)97 get_interp_param(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
98 {
99 const enum glsl_interp_mode mode = (enum glsl_interp_mode)nir_intrinsic_interp_mode(intrin);
100
101 if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_pixel) {
102 return nir_load_barycentric_pixel(b, 32, .interp_mode = mode);
103 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_at_offset) {
104 return lower_barycentric_coord_at_offset(b, intrin->src[0].ssa, mode);
105 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_at_sample) {
106 return lower_barycentric_coord_at_sample(b, state, intrin);
107 } else if (intrin->intrinsic == nir_intrinsic_load_barycentric_coord_centroid) {
108 return nir_load_barycentric_centroid(b, 32, .interp_mode = mode);
109 } else {
110 assert(intrin->intrinsic == nir_intrinsic_load_barycentric_coord_sample);
111 return nir_load_barycentric_sample(b, 32, .interp_mode = mode);
112 }
113
114 return NULL;
115 }
116
117 static nir_def *
lower_point(nir_builder * b)118 lower_point(nir_builder *b)
119 {
120 nir_def *coords[3];
121
122 coords[0] = nir_imm_float(b, 1.0f);
123 coords[1] = nir_imm_float(b, 0.0f);
124 coords[2] = nir_imm_float(b, 0.0f);
125
126 return nir_vec(b, coords, 3);
127 }
128
129 static nir_def *
lower_line(nir_builder * b,nir_def * p1,nir_def * p2)130 lower_line(nir_builder *b, nir_def *p1, nir_def *p2)
131 {
132 nir_def *coords[3];
133
134 coords[1] = nir_fadd(b, p1, p2);
135 coords[0] = nir_fsub_imm(b, 1.0f, coords[1]);
136 coords[2] = nir_imm_float(b, 0.0f);
137
138 return nir_vec(b, coords, 3);
139 }
140
141 static nir_def *
lower_triangle(nir_builder * b,nir_def * p1,nir_def * p2)142 lower_triangle(nir_builder *b, nir_def *p1, nir_def *p2)
143 {
144 nir_def *v0_bary[3], *v1_bary[3], *v2_bary[3];
145 nir_def *coords[3];
146
147 /* Compute the provoking vertex ID:
148 *
149 * quad_id = thread_id >> 2
150 * provoking_vtx_id = (provoking_vtx >> (quad_id << 1)) & 3
151 */
152 nir_def *quad_id = nir_ushr_imm(b, nir_load_subgroup_invocation(b), 2);
153 nir_def *provoking_vtx = nir_load_provoking_vtx_amd(b);
154 nir_def *provoking_vtx_id = nir_ubfe(b, provoking_vtx, nir_ishl_imm(b, quad_id, 1), nir_imm_int(b, 2));
155
156 /* Compute barycentrics. */
157 v0_bary[0] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
158 v0_bary[1] = p1;
159 v0_bary[2] = p2;
160
161 v1_bary[0] = p1;
162 v1_bary[1] = p2;
163 v1_bary[2] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
164
165 v2_bary[0] = p2;
166 v2_bary[1] = nir_fsub(b, nir_fsub_imm(b, 1.0f, p2), p1);
167 v2_bary[2] = p1;
168
169 /* Select barycentrics for the given provoking vertex ID. */
170 for (unsigned i = 0; i < 3; i++) {
171 coords[i] = nir_bcsel(b, nir_ieq_imm(b, provoking_vtx_id, 2), v2_bary[i],
172 nir_bcsel(b, nir_ieq_imm(b, provoking_vtx_id, 1), v1_bary[i], v0_bary[i]));
173 }
174
175 return nir_vec(b, coords, 3);
176 }
177
178 static bool
lower_load_barycentric_coord(nir_builder * b,lower_fs_barycentric_state * state,nir_intrinsic_instr * intrin)179 lower_load_barycentric_coord(nir_builder *b, lower_fs_barycentric_state *state, nir_intrinsic_instr *intrin)
180 {
181 nir_def *interp, *p1, *p2;
182 nir_def *new_dest;
183
184 b->cursor = nir_after_instr(&intrin->instr);
185
186 /* When the rasterization primitive isn't known at compile time (GPL), load it. */
187 if (state->rast_prim == -1) {
188 nir_def *rast_prim = nir_load_rasterization_primitive_amd(b);
189 nir_def *res1, *res2;
190
191 nir_def *is_point = nir_ieq_imm(b, rast_prim, V_028A6C_POINTLIST);
192 nir_if *if_point = nir_push_if(b, is_point);
193 {
194 res1 = lower_point(b);
195 }
196 nir_push_else(b, if_point);
197 {
198 nir_def *res_line, *res_triangle;
199
200 interp = get_interp_param(b, state, intrin);
201 p1 = nir_channel(b, interp, 0);
202 p2 = nir_channel(b, interp, 1);
203
204 nir_def *is_line = nir_ieq_imm(b, rast_prim, V_028A6C_LINESTRIP);
205 nir_if *if_line = nir_push_if(b, is_line);
206 {
207 res_line = lower_line(b, p1, p2);
208 }
209 nir_push_else(b, if_line);
210 {
211 res_triangle = lower_triangle(b, p1, p2);
212 }
213 nir_pop_if(b, if_line);
214
215 res2 = nir_if_phi(b, res_line, res_triangle);
216 }
217 nir_pop_if(b, if_point);
218
219 new_dest = nir_if_phi(b, res1, res2);
220 } else {
221 if (state->rast_prim == V_028A6C_POINTLIST) {
222 new_dest = lower_point(b);
223 } else {
224 interp = get_interp_param(b, state, intrin);
225 p1 = nir_channel(b, interp, 0);
226 p2 = nir_channel(b, interp, 1);
227
228 if (state->rast_prim == V_028A6C_LINESTRIP) {
229 new_dest = lower_line(b, p1, p2);
230 } else {
231 assert(state->rast_prim == V_028A6C_TRISTRIP);
232 new_dest = lower_triangle(b, p1, p2);
233 }
234 }
235 }
236
237 nir_def_replace(&intrin->def, new_dest);
238
239 return true;
240 }
241
242 bool
radv_nir_lower_fs_barycentric(nir_shader * shader,const struct radv_graphics_state_key * gfx_state,unsigned rast_prim)243 radv_nir_lower_fs_barycentric(nir_shader *shader, const struct radv_graphics_state_key *gfx_state, unsigned rast_prim)
244 {
245 nir_function_impl *impl = nir_shader_get_entrypoint(shader);
246 bool progress = false;
247
248 nir_builder b;
249
250 lower_fs_barycentric_state state = {
251 .dynamic_rasterization_samples = gfx_state->dynamic_rasterization_samples,
252 .num_rasterization_samples = gfx_state->ms.rasterization_samples,
253 .rast_prim = rast_prim,
254 };
255
256 nir_foreach_function (function, shader) {
257 if (!function->impl)
258 continue;
259
260 b = nir_builder_create(function->impl);
261
262 nir_foreach_block (block, impl) {
263 nir_foreach_instr_safe (instr, block) {
264 if (instr->type != nir_instr_type_intrinsic)
265 continue;
266
267 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
268 if (intrin->intrinsic != nir_intrinsic_load_barycentric_coord_pixel &&
269 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_centroid &&
270 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_sample &&
271 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_at_offset &&
272 intrin->intrinsic != nir_intrinsic_load_barycentric_coord_at_sample)
273 continue;
274
275 progress |= lower_load_barycentric_coord(&b, &state, intrin);
276 }
277 }
278 }
279
280 nir_metadata_preserve(impl, progress ? nir_metadata_none : nir_metadata_all);
281
282 return progress;
283 }
284