xref: /aosp_15_r20/external/mesa3d/src/amd/vulkan/nir/radv_nir_lower_fs_barycentric.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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