xref: /aosp_15_r20/external/mesa3d/src/panfrost/midgard/midgard_derivatives.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright (C) 2019 Collabora, Ltd.
3  * Copyright (C) 2019-2020 Collabora, Ltd.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * Authors (Collabora):
25  *   Alyssa Rosenzweig <[email protected]>
26  */
27 
28 #include "compiler.h"
29 
30 /* Derivatives in Midgard are implemented on the texture pipe, rather than the
31  * ALU pipe as suggested by NIR. The rationale is that normal texture
32  * instructions require (implicit) derivatives to be calculated anyway, so it
33  * makes sense to reuse the derivative logic. Thus, in addition to the usual
34  * texturing ops that calculate derivatives, there are two explicit texture ops
35  * dFdx/dFdy that perform differencing across helper invocations in either
36  * horizontal or vertical directions.
37  *
38  * One major caveat is that derivatives can only be calculated on up to a vec2
39  * at a time. This restriction presumably is to save some silicon, as 99% of
40  * derivatives will be vec2 (autocalculating mip levels of 2D texture
41  * coordinates). Admittedly I'm not sure why 3D textures can have their levels
42  * calculated automatically, umm... Pressing on.
43  *
44  * This caveat is handled in two steps. During the first pass (code
45  * generation), we generate texture ops 1:1 to the incoming NIR derivatives.
46  * This works for float/vec2 but not for vec3/vec4. A later lowering pass will
47  * scan for vec3/vec4 derivatives and lower (split) to multiple instructions.
48  * This pass is separated as we'll have to rewrite th e destination into a
49  * register (rather than SSA) and we'd rather do this after we have the whole
50  * IR in front of us to do it at once.
51  */
52 
53 static unsigned
mir_derivative_mode(nir_intrinsic_op op)54 mir_derivative_mode(nir_intrinsic_op op)
55 {
56    switch (op) {
57    case nir_intrinsic_ddx:
58    case nir_intrinsic_ddx_fine:
59    case nir_intrinsic_ddx_coarse:
60       return TEXTURE_DFDX;
61 
62    case nir_intrinsic_ddy:
63    case nir_intrinsic_ddy_fine:
64    case nir_intrinsic_ddy_coarse:
65       return TEXTURE_DFDY;
66 
67    default:
68       unreachable("Invalid derivative op");
69    }
70 }
71 
72 /* Returns true if a texturing op computes derivatives either explicitly or
73  * implicitly */
74 
75 bool
mir_op_computes_derivatives(gl_shader_stage stage,unsigned op)76 mir_op_computes_derivatives(gl_shader_stage stage, unsigned op)
77 {
78    /* Only fragment shaders may compute derivatives, but the sense of
79     * "normal" changes in vertex shaders on certain GPUs */
80 
81    if (op == midgard_tex_op_normal && stage != MESA_SHADER_FRAGMENT)
82       return false;
83 
84    switch (op) {
85    case midgard_tex_op_normal:
86    case midgard_tex_op_derivative:
87       assert(stage == MESA_SHADER_FRAGMENT);
88       return true;
89    default:
90       return false;
91    }
92 }
93 
94 void
midgard_emit_derivatives(compiler_context * ctx,nir_intrinsic_instr * instr)95 midgard_emit_derivatives(compiler_context *ctx, nir_intrinsic_instr *instr)
96 {
97    /* Create texture instructions */
98    midgard_instruction ins = {
99       .type = TAG_TEXTURE_4,
100       .dest_type = nir_type_float32,
101       .src =
102          {
103             ~0,
104             nir_src_index(ctx, &instr->src[0]),
105             ~0,
106             ~0,
107          },
108       .swizzle = SWIZZLE_IDENTITY_4,
109       .src_types =
110          {
111             nir_type_float32,
112             nir_type_float32,
113          },
114       .op = midgard_tex_op_derivative,
115       .texture =
116          {
117             .mode = mir_derivative_mode(instr->intrinsic),
118             .format = 2,
119             .in_reg_full = 1,
120             .out_full = 1,
121             .sampler_type = MALI_SAMPLER_FLOAT,
122          },
123    };
124 
125    ins.dest = nir_def_index_with_mask(&instr->def, &ins.mask);
126    emit_mir_instruction(ctx, ins);
127 }
128 
129 void
midgard_lower_derivatives(compiler_context * ctx,midgard_block * block)130 midgard_lower_derivatives(compiler_context *ctx, midgard_block *block)
131 {
132    mir_foreach_instr_in_block_safe(block, ins) {
133       if (ins->type != TAG_TEXTURE_4)
134          continue;
135       if (ins->op != midgard_tex_op_derivative)
136          continue;
137 
138       /* Check if we need to split */
139 
140       bool upper = ins->mask & 0b1100;
141       bool lower = ins->mask & 0b0011;
142 
143       if (!(upper && lower))
144          continue;
145 
146       /* Duplicate for dedicated upper instruction */
147 
148       midgard_instruction dup;
149       memcpy(&dup, ins, sizeof(dup));
150 
151       /* Fixup masks. Make original just lower and dupe just upper */
152 
153       ins->mask &= 0b0011;
154       dup.mask &= 0b1100;
155 
156       /* Fixup swizzles */
157       dup.swizzle[0][0] = dup.swizzle[0][1] = dup.swizzle[0][2] = COMPONENT_X;
158       dup.swizzle[0][3] = COMPONENT_Y;
159 
160       dup.swizzle[1][0] = COMPONENT_Z;
161       dup.swizzle[1][1] = dup.swizzle[1][2] = dup.swizzle[1][3] = COMPONENT_W;
162 
163       /* Insert the new instruction */
164       mir_insert_instruction_before(ctx, mir_next_op(ins), dup);
165 
166       /* We'll need both instructions to write to the same index, so
167        * rewrite to use a register */
168 
169       unsigned new = make_compiler_temp_reg(ctx);
170       mir_rewrite_index(ctx, ins->dest, new);
171    }
172 }
173