From 5d32cf642fcbbd61e1ceb862ff46949524252e86 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Mon, 3 Aug 2020 15:21:16 -0400 Subject: [PATCH] nir: Add varying precision linking helper (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It is useful for the precisions of varyings to match across shader stages at link-time to enable precision lowering optimizations, which would otherwise require costly draw-time fixups. The goal is to enable `producer->precision == consumer->precision` to be an invariant drivers may rely on for linked shaders. v2: keep transform feedback outputs at mediump - mareko Signed-off-by: Alyssa Rosenzweig (v1) Reviewed-by: Eric Anholt Reviewed-by: Marek Olšák Part-of: --- src/compiler/nir/nir.h | 1 + src/compiler/nir/nir_linking_helpers.c | 65 +++++++++++++++++++++++++++++++ src/mesa/state_tracker/st_glsl_to_nir.cpp | 2 + 3 files changed, 68 insertions(+) diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index bc386b5..2893188 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -4355,6 +4355,7 @@ void nir_compact_varyings(nir_shader *producer, nir_shader *consumer, bool default_to_smooth_interp); void nir_link_xfb_varyings(nir_shader *producer, nir_shader *consumer); bool nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer); +void nir_link_varying_precision(nir_shader *producer, nir_shader *consumer); bool nir_lower_amul(nir_shader *shader, int (*type_size)(const struct glsl_type *, bool)); diff --git a/src/compiler/nir/nir_linking_helpers.c b/src/compiler/nir/nir_linking_helpers.c index 2b3b16f..2b23d08 100644 --- a/src/compiler/nir/nir_linking_helpers.c +++ b/src/compiler/nir/nir_linking_helpers.c @@ -1001,6 +1001,71 @@ replace_duplicate_input(nir_shader *shader, nir_variable *input_var, return progress; } +/* The GLSL ES 3.20 spec says: + * + * "The precision of a vertex output does not need to match the precision of + * the corresponding fragment input. The minimum precision at which vertex + * outputs are interpolated is the minimum of the vertex output precision and + * the fragment input precision, with the exception that for highp, + * implementations do not have to support full IEEE 754 precision." (9.1 "Input + * Output Matching by Name in Linked Programs") + * + * To implement this, when linking shaders we will take the minimum precision + * qualifier (allowing drivers to interpolate at lower precision). For + * input/output between non-fragment stages (e.g. VERTEX to GEOMETRY), the spec + * requires we use the *last* specified precision if there is a conflict. + * + * Precisions are ordered as (NONE, HIGH, MEDIUM, LOW). If either precision is + * NONE, we'll return the other precision, since there is no conflict. + * Otherwise for fragment interpolation, we'll pick the smallest of (HIGH, + * MEDIUM, LOW) by picking the maximum of the raw values - note the ordering is + * "backwards". For non-fragment stages, we'll pick the latter precision to + * comply with the spec. (Note that the order matters.) + * + * For streamout, "Variables declared with lowp or mediump precision are + * promoted to highp before being written." (12.2 "Transform Feedback", p. 341 + * of OpenGL ES 3.2 specification). So drivers should promote them + * the transform feedback memory store, but not the output store. + */ + +static unsigned +nir_link_precision(unsigned producer, unsigned consumer, bool fs) +{ + if (producer == GLSL_PRECISION_NONE) + return consumer; + else if (consumer == GLSL_PRECISION_NONE) + return producer; + else + return fs ? MAX2(producer, consumer) : consumer; +} + +void +nir_link_varying_precision(nir_shader *producer, nir_shader *consumer) +{ + bool frag = consumer->info.stage == MESA_SHADER_FRAGMENT; + + nir_foreach_shader_out_variable(producer_var, producer) { + /* Skip if the slot is not assigned */ + if (producer_var->data.location < 0) + continue; + + nir_variable *consumer_var = nir_find_variable_with_location(consumer, + nir_var_shader_in, producer_var->data.location); + + /* Skip if the variable will be eliminated */ + if (!consumer_var) + continue; + + /* Now we have a pair of variables. Let's pick the smaller precision. */ + unsigned precision_1 = producer_var->data.precision; + unsigned precision_2 = consumer_var->data.precision; + unsigned minimum = nir_link_precision(precision_1, precision_2, frag); + + /* Propagate the new precision */ + producer_var->data.precision = consumer_var->data.precision = minimum; + } +} + bool nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer) { diff --git a/src/mesa/state_tracker/st_glsl_to_nir.cpp b/src/mesa/state_tracker/st_glsl_to_nir.cpp index 494ee7a..164304e 100644 --- a/src/mesa/state_tracker/st_glsl_to_nir.cpp +++ b/src/mesa/state_tracker/st_glsl_to_nir.cpp @@ -634,6 +634,8 @@ st_nir_link_shaders(nir_shader *producer, nir_shader *consumer) NIR_PASS_V(consumer, nir_remove_dead_variables, nir_var_shader_in, NULL); } + + nir_link_varying_precision(producer, consumer); } static void -- 2.7.4