diff options
-rw-r--r-- | src/broadcom/Makefile.sources | 1 | ||||
-rw-r--r-- | src/broadcom/compiler/meson.build | 1 | ||||
-rw-r--r-- | src/broadcom/compiler/v3d_compiler.h | 2 | ||||
-rw-r--r-- | src/broadcom/compiler/v3d_nir_lower_line_smooth.c | 163 | ||||
-rw-r--r-- | src/broadcom/compiler/vir.c | 7 | ||||
-rw-r--r-- | src/gallium/drivers/v3d/v3d_context.c | 45 | ||||
-rw-r--r-- | src/gallium/drivers/v3d/v3d_context.h | 4 | ||||
-rw-r--r-- | src/gallium/drivers/v3d/v3d_program.c | 2 | ||||
-rw-r--r-- | src/gallium/drivers/v3d/v3d_uniforms.c | 5 | ||||
-rw-r--r-- | src/gallium/drivers/v3d/v3dx_emit.c | 2 |
10 files changed, 230 insertions, 2 deletions
diff --git a/src/broadcom/Makefile.sources b/src/broadcom/Makefile.sources index e759b545496..e3ed63d6330 100644 --- a/src/broadcom/Makefile.sources +++ b/src/broadcom/Makefile.sources @@ -41,6 +41,7 @@ BROADCOM_FILES = \ compiler/v3d_compiler.h \ compiler/v3d_nir_lower_image_load_store.c \ compiler/v3d_nir_lower_io.c \ + compiler/v3d_nir_lower_line_smooth.c \ compiler/v3d_nir_lower_scratch.c \ compiler/v3d_nir_lower_txf_ms.c \ qpu/qpu_disasm.c \ diff --git a/src/broadcom/compiler/meson.build b/src/broadcom/compiler/meson.build index 9094f1ac6d0..43a312c6bea 100644 --- a/src/broadcom/compiler/meson.build +++ b/src/broadcom/compiler/meson.build @@ -37,6 +37,7 @@ libbroadcom_compiler_files = files( 'v3d_compiler.h', 'v3d_nir_lower_io.c', 'v3d_nir_lower_image_load_store.c', + 'v3d_nir_lower_line_smooth.c', 'v3d_nir_lower_logic_ops.c', 'v3d_nir_lower_scratch.c', 'v3d_nir_lower_txf_ms.c', diff --git a/src/broadcom/compiler/v3d_compiler.h b/src/broadcom/compiler/v3d_compiler.h index d6de62486d9..10df1af3ce4 100644 --- a/src/broadcom/compiler/v3d_compiler.h +++ b/src/broadcom/compiler/v3d_compiler.h @@ -352,6 +352,7 @@ struct v3d_fs_key { bool depth_enabled; bool is_points; bool is_lines; + bool line_smoothing; bool alpha_test; bool point_coord_upper_left; bool light_twoside; @@ -867,6 +868,7 @@ bool vir_opt_small_immediates(struct v3d_compile *c); bool vir_opt_vpm(struct v3d_compile *c); void v3d_nir_lower_blend(nir_shader *s, struct v3d_compile *c); void v3d_nir_lower_io(nir_shader *s, struct v3d_compile *c); +void v3d_nir_lower_line_smooth(nir_shader *shader); void v3d_nir_lower_logic_ops(nir_shader *s, struct v3d_compile *c); void v3d_nir_lower_scratch(nir_shader *s); void v3d_nir_lower_txf_ms(nir_shader *s, struct v3d_compile *c); diff --git a/src/broadcom/compiler/v3d_nir_lower_line_smooth.c b/src/broadcom/compiler/v3d_nir_lower_line_smooth.c new file mode 100644 index 00000000000..ace7d5cb5ca --- /dev/null +++ b/src/broadcom/compiler/v3d_nir_lower_line_smooth.c @@ -0,0 +1,163 @@ +/* + * Copyright © 2020 Raspberry Pi + * + * 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 void +lower_line_smooth_func(struct lower_line_smooth_state *state, + nir_function_impl *impl) +{ + 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); + } + } +} + +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_intrinsic_instr *discard = + nir_intrinsic_instr_create(state->shader, + nir_intrinsic_discard_if); + discard->src[0] = nir_src_for_ssa(outside); + nir_builder_instr_insert(&b, &discard->instr); + + /* 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; +} + +void +v3d_nir_lower_line_smooth(nir_shader *s) +{ + 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); + + lower_line_smooth_func(&state, function->impl); + } +} diff --git a/src/broadcom/compiler/vir.c b/src/broadcom/compiler/vir.c index ff2e0290122..d26a3250ead 100644 --- a/src/broadcom/compiler/vir.c +++ b/src/broadcom/compiler/vir.c @@ -890,6 +890,13 @@ v3d_nir_lower_fs_early(struct v3d_compile *c) NIR_PASS_V(c->s, v3d_nir_lower_logic_ops, c); + if (c->fs_key->line_smoothing) { + v3d_nir_lower_line_smooth(c->s); + NIR_PASS_V(c->s, nir_lower_global_vars_to_local); + /* The lowering pass can introduce new sysval reads */ + nir_shader_gather_info(c->s, nir_shader_get_entrypoint(c->s)); + } + /* If the shader has no non-TLB side effects, we can promote it to * enabling early_fragment_tests even if the user didn't. */ diff --git a/src/gallium/drivers/v3d/v3d_context.c b/src/gallium/drivers/v3d/v3d_context.c index f3dc3a92fec..185bdc687b4 100644 --- a/src/gallium/drivers/v3d/v3d_context.c +++ b/src/gallium/drivers/v3d/v3d_context.c @@ -149,6 +149,51 @@ v3d_update_primitive_counters(struct v3d_context *v3d) } } +bool +v3d_line_smoothing_enabled(struct v3d_context *v3d) +{ + if (!v3d->rasterizer->base.line_smooth) + return false; + + /* According to the OpenGL docs, line smoothing shouldn’t be applied + * when multisampling + */ + if (v3d->job->msaa || v3d->rasterizer->base.multisample) + return false; + + if (v3d->framebuffer.nr_cbufs <= 0) + return false; + + struct pipe_surface *cbuf = v3d->framebuffer.cbufs[0]; + if (!cbuf) + return false; + + /* Modifying the alpha for pure integer formats probably + * doesn’t make sense because we don’t know how the application + * uses the alpha value. + */ + if (util_format_is_pure_integer(cbuf->format)) + return false; + + return true; +} + +float +v3d_get_real_line_width(struct v3d_context *v3d) +{ + float width = v3d->rasterizer->base.line_width; + + if (v3d_line_smoothing_enabled(v3d)) { + /* If line smoothing is enabled then we want to add some extra + * pixels to the width in order to have some semi-transparent + * edges. + */ + width = floorf(M_SQRT2 * width) + 3; + } + + return width; +} + static void v3d_context_destroy(struct pipe_context *pctx) { diff --git a/src/gallium/drivers/v3d/v3d_context.h b/src/gallium/drivers/v3d/v3d_context.h index 55bc96bed9d..74df773ca9a 100644 --- a/src/gallium/drivers/v3d/v3d_context.h +++ b/src/gallium/drivers/v3d/v3d_context.h @@ -699,6 +699,10 @@ struct v3d_fence *v3d_fence_create(struct v3d_context *v3d); void v3d_update_primitive_counters(struct v3d_context *v3d); +bool v3d_line_smoothing_enabled(struct v3d_context *v3d); + +float v3d_get_real_line_width(struct v3d_context *v3d); + #ifdef v3dX # include "v3dx_context.h" #else diff --git a/src/gallium/drivers/v3d/v3d_program.c b/src/gallium/drivers/v3d/v3d_program.c index 2415b279754..07a596d3eaf 100644 --- a/src/gallium/drivers/v3d/v3d_program.c +++ b/src/gallium/drivers/v3d/v3d_program.c @@ -541,6 +541,8 @@ v3d_update_compiled_fs(struct v3d_context *v3d, uint8_t prim_mode) key->is_points = (prim_mode == PIPE_PRIM_POINTS); key->is_lines = (prim_mode >= PIPE_PRIM_LINES && prim_mode <= PIPE_PRIM_LINE_STRIP); + key->line_smoothing = (key->is_lines && + v3d_line_smoothing_enabled(v3d)); key->clamp_color = v3d->rasterizer->base.clamp_fragment_color; if (v3d->blend->base.logicop_enable) { key->logicop_func = v3d->blend->base.logicop_func; diff --git a/src/gallium/drivers/v3d/v3d_uniforms.c b/src/gallium/drivers/v3d/v3d_uniforms.c index 64fde274a99..37fb98daa57 100644 --- a/src/gallium/drivers/v3d/v3d_uniforms.c +++ b/src/gallium/drivers/v3d/v3d_uniforms.c @@ -313,11 +313,14 @@ v3d_write_uniforms(struct v3d_context *v3d, struct v3d_job *job, break; case QUNIFORM_LINE_WIDTH: - case QUNIFORM_AA_LINE_WIDTH: cl_aligned_f(&uniforms, v3d->rasterizer->base.line_width); break; + case QUNIFORM_AA_LINE_WIDTH: + cl_aligned_f(&uniforms, v3d_get_real_line_width(v3d)); + break; + case QUNIFORM_UBO_ADDR: { uint32_t unit = v3d_unit_data_get_unit(data); /* Constant buffer 0 may be a system memory pointer, diff --git a/src/gallium/drivers/v3d/v3dx_emit.c b/src/gallium/drivers/v3d/v3dx_emit.c index bcad6cddac6..2dad2e0e246 100644 --- a/src/gallium/drivers/v3d/v3dx_emit.c +++ b/src/gallium/drivers/v3d/v3dx_emit.c @@ -551,7 +551,7 @@ v3dX(emit_state)(struct pipe_context *pctx) } cl_emit(&job->bcl, LINE_WIDTH, line_width) { - line_width.line_width = v3d->rasterizer->base.line_width; + line_width.line_width = v3d_get_real_line_width(v3d); } } |