xref: /aosp_15_r20/external/mesa3d/src/freedreno/ir3/ir3_nir_lower_load_barycentric_at_offset.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2019 Google, Inc.
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "compiler/nir/nir_builder.h"
7 #include "ir3_nir.h"
8 
9 /**
10  * This pass lowers load_barycentric_at_offset to dsx.3d/dsy.3d and alu
11  * instructions.
12  */
13 
14 static nir_def *
ir3_nir_lower_load_barycentric_at_offset_instr(nir_builder * b,nir_instr * instr,void * data)15 ir3_nir_lower_load_barycentric_at_offset_instr(nir_builder *b, nir_instr *instr,
16                                                void *data)
17 {
18    nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
19    enum glsl_interp_mode interp_mode = nir_intrinsic_interp_mode(intr);
20 
21 #define chan(var, c) nir_channel(b, var, c)
22 
23    nir_def *off = intr->src[0].ssa;
24    /* note: at_offset is defined to be relative to the center of the pixel */
25    nir_def *ij = nir_load_barycentric_pixel(b, 32, .interp_mode = interp_mode);
26 
27    /* Need helper invocations for our ddx/ddys to work. */
28    if (b->shader->info.stage == MESA_SHADER_FRAGMENT)
29       b->shader->info.fs.needs_quad_helper_invocations = true;
30 
31    if (interp_mode != INTERP_MODE_SMOOTH) {
32       /* Offset our pixel center ij by the offset argument (units of pixels)
33        * times the derivatives of ij in screen space.
34        */
35       nir_def *new_ij = ij;
36       new_ij = nir_ffma(b, chan(off, 0), nir_ddx(b, ij), new_ij);
37       new_ij = nir_ffma(b, chan(off, 1), nir_ddy(b, ij), new_ij);
38 
39       return new_ij;
40    } else {
41       nir_def *center_w = nir_frcp(b, nir_load_persp_center_rhw_ir3(b, 32));
42 
43       /* scaled ij -- ij comes in multiplied by by 1/center_w so multiply that
44        * back out, plus add center_w as the 3rd component for taking the
45        * derivatives.
46        *
47        * We actually suspect that we should be using rhw here instead of center_w,
48        * but no tests seem to distinguish between the two.
49        */
50       nir_def *sij =
51          nir_vec3(b, nir_fmul(b, chan(ij, 0), center_w), nir_fmul(b, chan(ij, 1), center_w), center_w);
52 
53       /* Get the offset value from pixel center for ij, and also for w. */
54       nir_def *pos = sij;
55       pos = nir_ffma(b, chan(off, 0), nir_ddx(b, sij), pos);
56       pos = nir_ffma(b, chan(off, 1), nir_ddy(b, sij), pos);
57 
58       /* convert back into screen space, dividing by the offset 1/w */
59       return nir_fmul(b, nir_trim_vector(b, pos, 2),
60                       nir_frcp(b, chan(pos, 2)));
61    }
62 }
63 
64 static bool
ir3_nir_lower_load_barycentric_at_offset_filter(const nir_instr * instr,const void * data)65 ir3_nir_lower_load_barycentric_at_offset_filter(const nir_instr *instr,
66                                                 const void *data)
67 {
68    return (instr->type == nir_instr_type_intrinsic &&
69            nir_instr_as_intrinsic(instr)->intrinsic ==
70               nir_intrinsic_load_barycentric_at_offset);
71 }
72 
73 bool
ir3_nir_lower_load_barycentric_at_offset(nir_shader * shader)74 ir3_nir_lower_load_barycentric_at_offset(nir_shader *shader)
75 {
76    assert(shader->info.stage == MESA_SHADER_FRAGMENT);
77 
78    return nir_shader_lower_instructions(
79       shader, ir3_nir_lower_load_barycentric_at_offset_filter,
80       ir3_nir_lower_load_barycentric_at_offset_instr, NULL);
81 }
82