xref: /aosp_15_r20/external/mesa3d/src/asahi/compiler/agx_nir_lower_interpolation.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2023 Valve Corporation
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "compiler/shader_enums.h"
7 #include "agx_compile.h"
8 #include "nir.h"
9 #include "nir_builder.h"
10 #include "nir_builder_opcodes.h"
11 #include "nir_intrinsics.h"
12 #include "nir_intrinsics_indices.h"
13 
14 /*
15  * In AGX, the values of fragment shader inputs are represented as coefficient
16  * vectors <A, B, C>, which are dotted with <x, y, 1> to perform interpolation.
17  * x and y are relative to the tile. In other words, A and B are the
18  * screen-space partial derivatives of the input, and C is the value at the
19  * corner of the tile.
20  *
21  * For some interpolation modes, the dot product happens in the iterator
22  * hardware. Other modes are implemented in this file, by lowering to math on
23  * the coefficient vectors.
24  */
25 
26 /* XXX: It's not clear what this is for, but seems necessary */
27 static nir_def *
cf_valid(nir_builder * b,nir_def * cf)28 cf_valid(nir_builder *b, nir_def *cf)
29 {
30    nir_def *bit = nir_ieq_imm(b, nir_iand_imm(b, nir_channel(b, cf, 0), 1), 0);
31 
32    /* XXX: Apple's compiler actually checks that the significand is nonzero and
33     * the exponent is 0 or 1. This is probably a typo -- it doesn't make any
34     * logical sense.  Presumably they just meant to check for denorms, so let's
35     * do that. Either way the tests pass.
36     */
37    nir_def *cf01 = nir_trim_vector(b, cf, 2);
38    return nir_ior(b, bit, nir_fisnormal(b, cf01));
39 }
40 
41 static nir_def *
interpolate_at_offset(nir_builder * b,nir_def * cf,nir_def * offset,bool perspective)42 interpolate_at_offset(nir_builder *b, nir_def *cf, nir_def *offset,
43                       bool perspective)
44 {
45    /* Get the coordinate of the pixel within the tile */
46    nir_def *pixel_coords = nir_load_pixel_coord(b);
47    nir_def *tile_offs = nir_umod_imm(b, pixel_coords, 32);
48 
49    /* Convert to float, getting the center of the pixel */
50    nir_def *center = nir_fadd_imm(b, nir_u2f32(b, tile_offs), 0.5);
51 
52    /* Calculate the location to interpolate. offset is defined relative to the
53     * center of the pixel and is a float.
54     */
55    nir_def *pos = nir_fadd(b, center, nir_f2f32(b, offset));
56 
57    /* Interpolate with the given coefficients */
58    nir_def *interp = nir_ffma(b, nir_channel(b, pos, 1), nir_channel(b, cf, 1),
59                               nir_channel(b, cf, 2));
60 
61    interp = nir_ffma(b, nir_channel(b, pos, 0), nir_channel(b, cf, 0), interp);
62 
63    /* Divide by RHW. This load will be lowered recursively. */
64    if (perspective) {
65       nir_def *bary = nir_load_barycentric_at_offset(
66          b, 32, offset, .interp_mode = INTERP_MODE_NOPERSPECTIVE);
67 
68       nir_def *rhw = nir_load_interpolated_input(
69          b, 1, 32, bary, nir_imm_int(b, 0), .component = 3,
70          .io_semantics = {
71             .location = VARYING_SLOT_POS,
72             .num_slots = 1,
73          });
74 
75       interp = nir_fdiv(b, interp, rhw);
76    }
77 
78    /* Replace invalid interpolations with the constant channel */
79    return nir_bcsel(b, cf_valid(b, cf), interp, nir_channel(b, cf, 2));
80 }
81 
82 static nir_def *
interpolate_flat(nir_builder * b,nir_def * coefficients)83 interpolate_flat(nir_builder *b, nir_def *coefficients)
84 {
85    /* Same value anywhere, so just take the constant (affine) component. For
86     * triangle fans with the first provoking vertex, the CF layout is slightly
87     * different. I am unsure why, but Apple does the same and the bcsel is
88     * required for corrctness.
89     */
90    return nir_bcsel(b, nir_load_is_first_fan_agx(b),
91                     nir_channel(b, coefficients, 1),
92                     nir_channel(b, coefficients, 2));
93 }
94 
95 static enum glsl_interp_mode
interp_mode_for_load(nir_intrinsic_instr * load)96 interp_mode_for_load(nir_intrinsic_instr *load)
97 {
98    if (load->intrinsic == nir_intrinsic_load_input)
99       return INTERP_MODE_FLAT;
100    else
101       return nir_intrinsic_interp_mode(nir_src_as_intrinsic(load->src[0]));
102 }
103 
104 static bool
needs_lower(const nir_instr * instr,UNUSED const void * _)105 needs_lower(const nir_instr *instr, UNUSED const void *_)
106 {
107    if (instr->type != nir_instr_type_intrinsic)
108       return false;
109 
110    const nir_intrinsic_instr *load = nir_instr_as_intrinsic(instr);
111 
112    /* at_offset barycentrics need to be lowered */
113    if (load->intrinsic == nir_intrinsic_load_interpolated_input) {
114       return (nir_src_as_intrinsic(load->src[0])->intrinsic ==
115               nir_intrinsic_load_barycentric_at_offset);
116    }
117 
118    /* Flat shading always lowered */
119    return (load->intrinsic == nir_intrinsic_load_input);
120 }
121 
122 static nir_def *
interpolate_channel(nir_builder * b,nir_intrinsic_instr * load,unsigned channel)123 interpolate_channel(nir_builder *b, nir_intrinsic_instr *load, unsigned channel)
124 {
125    nir_def *coefficients = nir_load_coefficients_agx(
126       b, nir_get_io_offset_src(load)->ssa,
127       .component = nir_intrinsic_component(load) + channel,
128       .interp_mode = interp_mode_for_load(load),
129       .io_semantics = nir_intrinsic_io_semantics(load));
130 
131    if (load->intrinsic == nir_intrinsic_load_input) {
132       assert(load->def.bit_size == 32);
133 
134       if (nir_intrinsic_io_semantics(load).location == VARYING_SLOT_LAYER)
135          return nir_load_layer_id(b);
136       else
137          return interpolate_flat(b, coefficients);
138    } else {
139       nir_intrinsic_instr *bary = nir_src_as_intrinsic(load->src[0]);
140 
141       nir_def *interp = interpolate_at_offset(
142          b, coefficients, bary->src[0].ssa,
143          nir_intrinsic_interp_mode(bary) != INTERP_MODE_NOPERSPECTIVE);
144 
145       return nir_f2fN(b, interp, load->def.bit_size);
146    }
147 }
148 
149 static nir_def *
lower(nir_builder * b,nir_instr * instr,void * data)150 lower(nir_builder *b, nir_instr *instr, void *data)
151 {
152    nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
153 
154    /* Each component is loaded separated */
155    nir_def *values[NIR_MAX_VEC_COMPONENTS] = {NULL};
156    for (unsigned i = 0; i < intr->def.num_components; ++i) {
157       values[i] = interpolate_channel(b, intr, i);
158    }
159 
160    return nir_vec(b, values, intr->def.num_components);
161 }
162 
163 bool
agx_nir_lower_interpolation(nir_shader * s)164 agx_nir_lower_interpolation(nir_shader *s)
165 {
166    assert(s->info.stage == MESA_SHADER_FRAGMENT);
167 
168    return nir_shader_lower_instructions(s, needs_lower, lower, NULL);
169 }
170