From c34295b1a3e2bd6ddf8a79bbe391aae1e98cd976 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 22 Feb 2017 16:53:18 -0800 Subject: [PATCH] nir: Move vc4's alpha test lowering to core NIR. I've been doing this inside of vc4, but vc5 wants it as well and it may be useful for other drivers (Intel has a related path for pre-gen6 with MRT, and freedreno had a TGSI path for it at one point). This required defining a common enum for the standard comparison functions, but other lowering passes are likely to also want that enum. v2: Add to meson.build as well. Acked-by: Rob Clark --- src/compiler/Makefile.sources | 1 + src/compiler/nir/meson.build | 1 + src/compiler/nir/nir.h | 2 + src/compiler/nir/nir_builder.h | 25 ++++ src/compiler/nir/nir_lower_alpha_test.c | 111 ++++++++++++++++++ src/compiler/shader_enums.h | 17 +++ src/gallium/drivers/vc4/vc4_nir_lower_blend.c | 50 -------- src/gallium/drivers/vc4/vc4_program.c | 15 ++- src/gallium/drivers/vc4/vc4_qir.h | 1 - 9 files changed, 168 insertions(+), 55 deletions(-) create mode 100644 src/compiler/nir/nir_lower_alpha_test.c diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index 36906f41dd5..352631a75ad 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -207,6 +207,7 @@ NIR_FILES = \ nir/nir_loop_analyze.c \ nir/nir_loop_analyze.h \ nir/nir_lower_64bit_packing.c \ + nir/nir_lower_alpha_test.c \ nir/nir_lower_alu_to_scalar.c \ nir/nir_lower_atomics.c \ nir/nir_lower_atomics_to_ssbo.c \ diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build index faa69e3aa30..144cf01d2c4 100644 --- a/src/compiler/nir/meson.build +++ b/src/compiler/nir/meson.build @@ -96,6 +96,7 @@ files_libnir = files( 'nir_loop_analyze.h', 'nir_lower_64bit_packing.c', 'nir_lower_alu_to_scalar.c', + 'nir_lower_alpha_test.c', 'nir_lower_atomics.c', 'nir_lower_atomics_to_ssbo.c', 'nir_lower_bitmap.c', diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index bb5aba605a1..5af150310f2 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -2447,6 +2447,8 @@ bool nir_lower_constant_initializers(nir_shader *shader, bool nir_move_vec_src_uses_to_dest(nir_shader *shader); bool nir_lower_vec_to_movs(nir_shader *shader); +void nir_lower_alpha_test(nir_shader *shader, enum compare_func func, + bool alpha_to_one); bool nir_lower_alu_to_scalar(nir_shader *shader); bool nir_lower_load_const_to_scalar(nir_shader *shader); bool nir_lower_read_invocation_to_scalar(nir_shader *shader); diff --git a/src/compiler/nir/nir_builder.h b/src/compiler/nir/nir_builder.h index 7c65886356d..4bd5628ff7d 100644 --- a/src/compiler/nir/nir_builder.h +++ b/src/compiler/nir/nir_builder.h @@ -643,4 +643,29 @@ nir_jump(nir_builder *build, nir_jump_type jump_type) nir_builder_instr_insert(build, &jump->instr); } +static inline nir_ssa_def * +nir_compare_func(nir_builder *b, enum compare_func func, + nir_ssa_def *src0, nir_ssa_def *src1) +{ + switch (func) { + case COMPARE_FUNC_NEVER: + return nir_imm_int(b, 0); + case COMPARE_FUNC_ALWAYS: + return nir_imm_int(b, ~0); + case COMPARE_FUNC_EQUAL: + return nir_feq(b, src0, src1); + case COMPARE_FUNC_NOTEQUAL: + return nir_fne(b, src0, src1); + case COMPARE_FUNC_GREATER: + return nir_flt(b, src1, src0); + case COMPARE_FUNC_GEQUAL: + return nir_fge(b, src0, src1); + case COMPARE_FUNC_LESS: + return nir_flt(b, src0, src1); + case COMPARE_FUNC_LEQUAL: + return nir_fge(b, src1, src0); + } + unreachable("bad compare func"); +} + #endif /* NIR_BUILDER_H */ diff --git a/src/compiler/nir/nir_lower_alpha_test.c b/src/compiler/nir/nir_lower_alpha_test.c new file mode 100644 index 00000000000..bd433b8ec66 --- /dev/null +++ b/src/compiler/nir/nir_lower_alpha_test.c @@ -0,0 +1,111 @@ +/* + * Copyright © 2017 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * @file + * + * Implements GL alpha testing by comparing the output color's alpha to the + * alpha_ref intrinsic and emitting a discard based on it. + * + * The alpha_to_one value overrides the source alpha to 1.0 to implement + * GL_SAMPLE_ALPHA_TO_ONE, which applies before the alpha test (and would be + * rather silly to use with alpha test, but the spec permits). + */ + +#include "nir/nir.h" +#include "nir/nir_builder.h" + +void +nir_lower_alpha_test(nir_shader *shader, enum compare_func func, + bool alpha_to_one) +{ + assert(shader->stage == MESA_SHADER_FRAGMENT); + + nir_foreach_function(function, shader) { + nir_function_impl *impl = function->impl; + nir_builder b; + nir_builder_init(&b, impl); + b.cursor = nir_before_cf_list(&impl->body); + + nir_foreach_block(block, impl) { + nir_foreach_instr_safe(instr, block) { + if (instr->type == nir_instr_type_intrinsic) { + nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr); + + nir_variable *out = NULL; + + switch (intr->intrinsic) { + case nir_intrinsic_store_var: + out = intr->variables[0]->var; + break; + case nir_intrinsic_store_output: + /* already had i/o lowered.. lookup the matching output var: */ + nir_foreach_variable(var, &shader->outputs) { + int drvloc = var->data.driver_location; + if (nir_intrinsic_base(intr) == drvloc) { + out = var; + break; + } + } + assume(out); + break; + default: + continue; + } + + if (out->data.mode != nir_var_shader_out) + continue; + + if (out->data.location != FRAG_RESULT_COLOR && + out->data.location != FRAG_RESULT_DATA0) + continue; + + b.cursor = nir_before_instr(&intr->instr); + + nir_ssa_def *alpha; + if (alpha_to_one) { + alpha = nir_imm_float(&b, 1.0); + } else { + alpha = nir_channel(&b, nir_ssa_for_src(&b, intr->src[0], 4), + 3); + } + + nir_ssa_def *condition = + nir_compare_func(&b, func, + alpha, nir_load_alpha_ref_float(&b)); + + nir_intrinsic_instr *discard = + nir_intrinsic_instr_create(b.shader, + nir_intrinsic_discard_if); + discard->num_components = 1; + discard->src[0] = nir_src_for_ssa(nir_inot(&b, condition)); + nir_builder_instr_insert(&b, &discard->instr); + shader->info.fs.uses_discard = true; + } + } + } + + nir_metadata_preserve(impl, nir_metadata_block_index | + nir_metadata_dominance); + } +} diff --git a/src/compiler/shader_enums.h b/src/compiler/shader_enums.h index 2f20e68c5d6..9d229d4199e 100644 --- a/src/compiler/shader_enums.h +++ b/src/compiler/shader_enums.h @@ -670,6 +670,23 @@ enum gl_tess_spacing TESS_SPACING_FRACTIONAL_EVEN, }; +/** + * A compare function enum for use in compiler lowering passes. This is in + * the same order as GL's compare functions (shifted down by GL_NEVER), and is + * exactly the same as gallium's PIPE_FUNC_*. + */ +enum compare_func +{ + COMPARE_FUNC_NEVER, + COMPARE_FUNC_LESS, + COMPARE_FUNC_EQUAL, + COMPARE_FUNC_LEQUAL, + COMPARE_FUNC_GREATER, + COMPARE_FUNC_NOTEQUAL, + COMPARE_FUNC_GEQUAL, + COMPARE_FUNC_ALWAYS, +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/gallium/drivers/vc4/vc4_nir_lower_blend.c b/src/gallium/drivers/vc4/vc4_nir_lower_blend.c index a28ebb5bb7c..60eccb4fc00 100644 --- a/src/gallium/drivers/vc4/vc4_nir_lower_blend.c +++ b/src/gallium/drivers/vc4/vc4_nir_lower_blend.c @@ -449,54 +449,6 @@ vc4_logicop(nir_builder *b, int logicop_func, } } -static nir_ssa_def * -vc4_nir_pipe_compare_func(nir_builder *b, int func, - nir_ssa_def *src0, nir_ssa_def *src1) -{ - switch (func) { - default: - fprintf(stderr, "Unknown compare func %d\n", func); - /* FALLTHROUGH */ - case PIPE_FUNC_NEVER: - return nir_imm_int(b, 0); - case PIPE_FUNC_ALWAYS: - return nir_imm_int(b, ~0); - case PIPE_FUNC_EQUAL: - return nir_feq(b, src0, src1); - case PIPE_FUNC_NOTEQUAL: - return nir_fne(b, src0, src1); - case PIPE_FUNC_GREATER: - return nir_flt(b, src1, src0); - case PIPE_FUNC_GEQUAL: - return nir_fge(b, src0, src1); - case PIPE_FUNC_LESS: - return nir_flt(b, src0, src1); - case PIPE_FUNC_LEQUAL: - return nir_fge(b, src1, src0); - } -} - -static void -vc4_nir_emit_alpha_test_discard(struct vc4_compile *c, nir_builder *b, - nir_ssa_def *alpha) -{ - if (!c->fs_key->alpha_test) - return; - - nir_ssa_def *condition = - vc4_nir_pipe_compare_func(b, c->fs_key->alpha_test_func, - alpha, - nir_load_alpha_ref_float(b)); - - nir_intrinsic_instr *discard = - nir_intrinsic_instr_create(b->shader, - nir_intrinsic_discard_if); - discard->num_components = 1; - discard->src[0] = nir_src_for_ssa(nir_inot(b, condition)); - nir_builder_instr_insert(b, &discard->instr); - c->s->info.fs.uses_discard = true; -} - static nir_ssa_def * vc4_nir_swizzle_and_pack(struct vc4_compile *c, nir_builder *b, nir_ssa_def **colors) @@ -537,8 +489,6 @@ vc4_nir_blend_pipeline(struct vc4_compile *c, nir_builder *b, nir_ssa_def *src, if (c->fs_key->sample_alpha_to_one && c->fs_key->msaa) src_color[3] = nir_imm_float(b, 1.0); - vc4_nir_emit_alpha_test_discard(c, b, src_color[3]); - nir_ssa_def *packed_color; if (srgb) { /* Unswizzle the destination color. */ diff --git a/src/gallium/drivers/vc4/vc4_program.c b/src/gallium/drivers/vc4/vc4_program.c index e93333d35e7..bf7424bf28a 100644 --- a/src/gallium/drivers/vc4/vc4_program.c +++ b/src/gallium/drivers/vc4/vc4_program.c @@ -2250,8 +2250,15 @@ vc4_shader_ntq(struct vc4_context *vc4, enum qstage stage, c->s = nir_shader_clone(c, key->shader_state->base.ir.nir); - if (stage == QSTAGE_FRAG) + if (stage == QSTAGE_FRAG) { + if (c->fs_key->alpha_test_func != COMPARE_FUNC_ALWAYS) { + NIR_PASS_V(c->s, nir_lower_alpha_test, + c->fs_key->alpha_test_func, + c->fs_key->sample_alpha_to_one && + c->fs_key->msaa); + } NIR_PASS_V(c->s, vc4_nir_lower_blend, c); + } struct nir_lower_tex_options tex_options = { /* We would need to implement txs, but we don't want the @@ -2748,10 +2755,10 @@ vc4_update_compiled_fs(struct vc4_context *vc4, uint8_t prim_mode) key->stencil_full_writemasks = vc4->zsa->stencil_uniforms[2] != 0; key->depth_enabled = (vc4->zsa->base.depth.enabled || key->stencil_enabled); - if (vc4->zsa->base.alpha.enabled) { - key->alpha_test = true; + if (vc4->zsa->base.alpha.enabled) key->alpha_test_func = vc4->zsa->base.alpha.func; - } + else + key->alpha_test_func = COMPARE_FUNC_ALWAYS; if (key->is_points) { key->point_sprite_mask = diff --git a/src/gallium/drivers/vc4/vc4_qir.h b/src/gallium/drivers/vc4/vc4_qir.h index 6469e51b051..90acaef2898 100644 --- a/src/gallium/drivers/vc4/vc4_qir.h +++ b/src/gallium/drivers/vc4/vc4_qir.h @@ -354,7 +354,6 @@ struct vc4_fs_key { bool stencil_full_writemasks; bool is_points; bool is_lines; - bool alpha_test; bool point_coord_upper_left; bool light_twoside; bool msaa; -- 2.30.2