| /* |
| * Copyright © 2020 Raspberry Pi Ltd |
| * |
| * 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. |
| */ |
| |
| #include "compiler/v3d_compiler.h" |
| #include "compiler/nir/nir_builder.h" |
| #include <math.h> |
| |
| /** |
| * Lowers line smoothing by modifying the alpha component of fragment outputs |
| * using the distance from the center of the line. |
| */ |
| |
| struct lower_line_smooth_state { |
| nir_shader *shader; |
| nir_variable *coverage; |
| }; |
| |
| static void |
| lower_line_smooth_intrinsic(struct lower_line_smooth_state *state, |
| nir_builder *b, |
| nir_intrinsic_instr *intr) |
| { |
| b->cursor = nir_before_instr(&intr->instr); |
| |
| nir_ssa_def *one = nir_imm_float(b, 1.0f); |
| |
| nir_ssa_def *coverage = nir_load_var(b, state->coverage); |
| |
| nir_ssa_def *new_val = nir_fmul(b, nir_vec4(b, one, one, one, coverage), |
| intr->src[0].ssa); |
| |
| nir_instr_rewrite_src(&intr->instr, |
| &intr->src[0], |
| nir_src_for_ssa(new_val)); |
| } |
| |
| static bool |
| lower_line_smooth_func(struct lower_line_smooth_state *state, |
| nir_function_impl *impl) |
| { |
| bool progress = false; |
| |
| nir_builder b; |
| |
| nir_builder_init(&b, impl); |
| |
| nir_foreach_block(block, impl) { |
| nir_foreach_instr_safe(instr, block) { |
| if (instr->type != nir_instr_type_intrinsic) |
| continue; |
| |
| nir_intrinsic_instr *intr = |
| nir_instr_as_intrinsic(instr); |
| |
| if (intr->intrinsic != nir_intrinsic_store_output || |
| nir_intrinsic_base(intr) != 0 || |
| intr->num_components != 4 || |
| !intr->src[0].is_ssa) |
| continue; |
| |
| lower_line_smooth_intrinsic(state, &b, intr); |
| progress = true; |
| } |
| } |
| |
| return progress; |
| } |
| |
| static void |
| initialise_coverage_var(struct lower_line_smooth_state *state, |
| nir_function_impl *impl) |
| { |
| nir_builder b; |
| |
| nir_builder_init(&b, impl); |
| |
| b.cursor = nir_before_block(nir_start_block(impl)); |
| |
| nir_ssa_def *line_width = nir_load_line_width(&b); |
| |
| nir_ssa_def *real_line_width = nir_load_aa_line_width(&b); |
| |
| /* The line coord varies from 0.0 to 1.0 across the width of the line */ |
| nir_ssa_def *line_coord = nir_load_line_coord(&b); |
| |
| /* fabs(line_coord - 0.5) * real_line_width */ |
| nir_ssa_def *pixels_from_center = |
| nir_fmul(&b, real_line_width, |
| nir_fabs(&b, nir_fsub(&b, line_coord, |
| nir_imm_float(&b, 0.5f)))); |
| |
| /* 0.5 - 1/√2 * (pixels_from_center - line_width * 0.5) */ |
| nir_ssa_def *coverage = |
| nir_fsub(&b, |
| nir_imm_float(&b, 0.5f), |
| nir_fmul(&b, |
| nir_imm_float(&b, 1.0f / M_SQRT2), |
| nir_fsub(&b, pixels_from_center, |
| nir_fmul(&b, |
| line_width, |
| nir_imm_float(&b, 0.5f))))); |
| |
| /* Discard fragments that aren’t covered at all by the line */ |
| nir_ssa_def *outside = nir_fge(&b, nir_imm_float(&b, 0.0f), coverage); |
| |
| nir_discard_if(&b, outside); |
| |
| /* Clamp to at most 1.0. If it was less than 0.0 then the fragment will |
| * be discarded so we don’t need to handle that. |
| */ |
| nir_ssa_def *clamped = nir_fmin(&b, coverage, nir_imm_float(&b, 1.0f)); |
| |
| nir_store_var(&b, state->coverage, clamped, 0x1 /* writemask */); |
| } |
| |
| static nir_variable * |
| make_coverage_var(nir_shader *s) |
| { |
| nir_variable *var = nir_variable_create(s, |
| nir_var_shader_temp, |
| glsl_float_type(), |
| "line_coverage"); |
| var->data.how_declared = nir_var_hidden; |
| |
| return var; |
| } |
| |
| bool |
| v3d_nir_lower_line_smooth(nir_shader *s) |
| { |
| bool progress = false; |
| |
| assert(s->info.stage == MESA_SHADER_FRAGMENT); |
| |
| struct lower_line_smooth_state state = { |
| .shader = s, |
| .coverage = make_coverage_var(s), |
| }; |
| |
| nir_foreach_function(function, s) { |
| if (function->is_entrypoint) |
| initialise_coverage_var(&state, function->impl); |
| |
| progress |= lower_line_smooth_func(&state, function->impl); |
| |
| if (progress) { |
| nir_metadata_preserve(function->impl, |
| nir_metadata_block_index | |
| nir_metadata_dominance); |
| } else { |
| nir_metadata_preserve(function->impl, nir_metadata_all); |
| } |
| } |
| |
| return progress; |
| } |