1/*
2 * Copyright © 2017 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24/**
25 * @file
26 *
27 * Implements GL alpha testing by comparing the output color's alpha to the
28 * alpha_ref intrinsic and emitting a discard based on it.
29 *
30 * The alpha_to_one value overrides the source alpha to 1.0 to implement
31 * GL_SAMPLE_ALPHA_TO_ONE, which applies before the alpha test (and would be
32 * rather silly to use with alpha test, but the spec permits).
33 */
34
35#include "nir/nir.h"
36#include "nir/nir_builder.h"
37
38void
39nir_lower_alpha_test(nir_shader *shader, enum compare_func func,
40                     bool alpha_to_one)
41{
42   assert(shader->info.stage == MESA_SHADER_FRAGMENT);
43
44   nir_foreach_function(function, shader) {
45      nir_function_impl *impl = function->impl;
46      nir_builder b;
47      nir_builder_init(&b, impl);
48      b.cursor = nir_before_cf_list(&impl->body);
49
50      nir_foreach_block(block, impl) {
51         nir_foreach_instr_safe(instr, block) {
52            if (instr->type == nir_instr_type_intrinsic) {
53               nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
54
55               nir_variable *out = NULL;
56
57               switch (intr->intrinsic) {
58               case nir_intrinsic_store_deref:
59                  out = nir_deref_instr_get_variable(nir_src_as_deref(intr->src[0]));
60                  break;
61               case nir_intrinsic_store_output:
62                  /* already had i/o lowered.. lookup the matching output var: */
63                  nir_foreach_variable(var, &shader->outputs) {
64                     int drvloc = var->data.driver_location;
65                     if (nir_intrinsic_base(intr) == drvloc) {
66                        out = var;
67                        break;
68                     }
69                  }
70                  assume(out);
71                  break;
72               default:
73                  continue;
74               }
75
76               if (out->data.mode != nir_var_shader_out)
77                  continue;
78
79               if (out->data.location != FRAG_RESULT_COLOR &&
80                   out->data.location != FRAG_RESULT_DATA0)
81                  continue;
82
83               b.cursor = nir_before_instr(&intr->instr);
84
85               nir_ssa_def *alpha;
86               if (alpha_to_one) {
87                  alpha = nir_imm_float(&b, 1.0);
88               } else if (intr->intrinsic == nir_intrinsic_store_deref) {
89                  alpha = nir_channel(&b, nir_ssa_for_src(&b, intr->src[1], 4),
90                                      3);
91               } else {
92                  alpha = nir_channel(&b, nir_ssa_for_src(&b, intr->src[0], 4),
93                                      3);
94               }
95
96               nir_ssa_def *condition =
97                  nir_compare_func(&b, func,
98                                   alpha, nir_load_alpha_ref_float(&b));
99
100               nir_intrinsic_instr *discard =
101                  nir_intrinsic_instr_create(b.shader,
102                                             nir_intrinsic_discard_if);
103               discard->num_components = 1;
104               discard->src[0] = nir_src_for_ssa(nir_inot(&b, condition));
105               nir_builder_instr_insert(&b, &discard->instr);
106               shader->info.fs.uses_discard = true;
107            }
108         }
109      }
110
111      nir_metadata_preserve(impl, nir_metadata_block_index |
112                            nir_metadata_dominance);
113   }
114}
115