aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/broadcom/Makefile.sources1
-rw-r--r--src/broadcom/compiler/meson.build1
-rw-r--r--src/broadcom/compiler/v3d_compiler.h2
-rw-r--r--src/broadcom/compiler/v3d_nir_lower_line_smooth.c163
-rw-r--r--src/broadcom/compiler/vir.c7
-rw-r--r--src/gallium/drivers/v3d/v3d_context.c45
-rw-r--r--src/gallium/drivers/v3d/v3d_context.h4
-rw-r--r--src/gallium/drivers/v3d/v3d_program.c2
-rw-r--r--src/gallium/drivers/v3d/v3d_uniforms.c5
-rw-r--r--src/gallium/drivers/v3d/v3dx_emit.c2
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);
}
}