• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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