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 nir_def *z = nir_load_frag_coord_zw(b, .component = 2);
26
27 nir_store_output(b, z, nir_imm_int(b, 0),
28 .io_semantics.location = FRAG_RESULT_DEPTH,
29 .src_type = nir_type_float32);
30
31 b->shader->info.outputs_written |= BITFIELD64_BIT(FRAG_RESULT_DEPTH);
32 }
33
34 static bool
pass(struct nir_builder * b,nir_intrinsic_instr * intr,void * data)35 pass(struct nir_builder *b, nir_intrinsic_instr *intr, void *data)
36 {
37 if (intr->intrinsic != nir_intrinsic_store_output)
38 return false;
39
40 /* Only lower once */
41 bool *done = data;
42 if (*done)
43 return false;
44 *done = true;
45
46 b->cursor = nir_before_instr(&intr->instr);
47 insert_z_write(b);
48 return true;
49 }
50
51 bool
agx_nir_lower_frag_sidefx(nir_shader * s)52 agx_nir_lower_frag_sidefx(nir_shader *s)
53 {
54 assert(s->info.stage == MESA_SHADER_FRAGMENT);
55
56 /* If there are no side effects, there's nothing to lower */
57 if (!s->info.writes_memory)
58 return false;
59
60 /* Lower writes from helper invocations with the common pass */
61 NIR_PASS(_, s, nir_lower_helper_writes, false);
62
63 bool writes_zs =
64 s->info.outputs_written &
65 (BITFIELD64_BIT(FRAG_RESULT_STENCIL) | BITFIELD64_BIT(FRAG_RESULT_DEPTH));
66
67 /* If the shader wants early fragment tests, the sample mask lowering pass
68 * will trigger an early test at the beginning of the shader. This lets us
69 * use a Passthrough punch type, instead of Opaque which may result in the
70 * shader getting skipped incorrectly and then the side effects not kicking
71 * in. But this happens there to avoid it happening twice with a discard.
72 */
73 if (s->info.fs.early_fragment_tests)
74 return false;
75
76 /* If depth/stencil feedback is already used, we're done */
77 if (writes_zs)
78 return false;
79
80 bool done = false;
81 nir_shader_intrinsics_pass(
82 s, pass, nir_metadata_block_index | nir_metadata_dominance, &done);
83
84 /* If there's no render targets written, just put the write at the end */
85 if (!done) {
86 nir_function_impl *impl = nir_shader_get_entrypoint(s);
87 nir_builder b = nir_builder_at(nir_after_impl(impl));
88
89 insert_z_write(&b);
90 }
91
92 return true;
93 }
94