xref: /aosp_15_r20/external/mesa3d/src/asahi/compiler/agx_nir_lower_frag_sidefx.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2022 Alyssa Rosenzweig
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "compiler/nir/nir.h"
7 #include "compiler/nir/nir_builder.h"
8 #include "agx_compiler.h"
9 
10 /* Fragment shaders with side effects require special handling to ensure the
11  * side effects execute as intended. By default, they require late depth
12  * testing, to ensure the side effects happen even for killed pixels. To handle,
13  * the driver inserts a dummy `gl_FragDepth = gl_Position.z` in shaders that
14  * don't otherwise write their depth, forcing a late depth test.
15  *
16  * For side effects with force early testing forced, the sample mask is written
17  * at the *beginning* of the shader.
18  */
19 
20 #define ALL_SAMPLES (0xFF)
21 
22 static void
insert_z_write(nir_builder * b)23 insert_z_write(nir_builder *b)
24 {
25    /* When forcing depth test to NEVER, force Z to NaN.  This works around the
26     * hardware discarding depth=NEVER.
27     *
28     * TODO: Check if Apple has a better way of handling this.
29     */
30    nir_def *bias = nir_bcsel(b, nir_ine_imm(b, nir_load_depth_never_agx(b), 0),
31                              nir_imm_float(b, NAN), nir_imm_float(b, -0.0));
32 
33    nir_def *z = nir_fadd(b, bias, nir_load_frag_coord_zw(b, .component = 2));
34 
35    nir_store_output(b, z, nir_imm_int(b, 0),
36                     .io_semantics.location = FRAG_RESULT_DEPTH,
37                     .src_type = nir_type_float32);
38 
39    b->shader->info.outputs_written |= BITFIELD64_BIT(FRAG_RESULT_DEPTH);
40 }
41 
42 static bool
pass(struct nir_builder * b,nir_intrinsic_instr * intr,void * data)43 pass(struct nir_builder *b, nir_intrinsic_instr *intr, void *data)
44 {
45    if (intr->intrinsic != nir_intrinsic_store_output)
46       return false;
47 
48    /* Only lower once */
49    bool *done = data;
50    if (*done)
51       return false;
52    *done = true;
53 
54    b->cursor = nir_before_instr(&intr->instr);
55    insert_z_write(b);
56    return true;
57 }
58 
59 bool
agx_nir_lower_frag_sidefx(nir_shader * s)60 agx_nir_lower_frag_sidefx(nir_shader *s)
61 {
62    assert(s->info.stage == MESA_SHADER_FRAGMENT);
63 
64    /* If there are no side effects, there's nothing to lower */
65    if (!s->info.writes_memory)
66       return false;
67 
68    /* Lower writes from helper invocations with the common pass. The hardware
69     * will predicate simple writes for us, but that fails with sample shading so
70     * we do that in software too.
71     */
72    NIR_PASS(_, s, nir_lower_helper_writes, s->info.fs.uses_sample_shading);
73 
74    bool writes_zs =
75       s->info.outputs_written &
76       (BITFIELD64_BIT(FRAG_RESULT_STENCIL) | BITFIELD64_BIT(FRAG_RESULT_DEPTH));
77 
78    /* If the shader wants early fragment tests, the sample mask lowering pass
79     * will trigger an early test at the beginning of the shader. This lets us
80     * use a Passthrough punch type, instead of Opaque which may result in the
81     * shader getting skipped incorrectly and then the side effects not kicking
82     * in. But this happens there to avoid it happening twice with a discard.
83     */
84    if (s->info.fs.early_fragment_tests)
85       return false;
86 
87    /* If depth/stencil feedback is already used, we're done */
88    if (writes_zs)
89       return false;
90 
91    bool done = false;
92    nir_shader_intrinsics_pass(s, pass, nir_metadata_control_flow, &done);
93 
94    /* If there's no render targets written, just put the write at the end */
95    if (!done) {
96       nir_function_impl *impl = nir_shader_get_entrypoint(s);
97       nir_builder b = nir_builder_at(nir_after_impl(impl));
98 
99       insert_z_write(&b);
100    }
101 
102    return true;
103 }
104