nir: Add varying precision linking helper (v2)

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 <alyssa.rosenzweig@collabora.com> (v1)
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9050>
This commit is contained in:
Alyssa Rosenzweig
2020-08-03 15:21:16 -04:00
committed by Marge Bot
parent fb29cef8dd
commit 5d32cf642f
3 changed files with 68 additions and 0 deletions

View File

@@ -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));

View File

@@ -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)
{

View File

@@ -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