• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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