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:

committed by
Marge Bot

parent
fb29cef8dd
commit
5d32cf642f
@@ -4355,6 +4355,7 @@ void nir_compact_varyings(nir_shader *producer, nir_shader *consumer,
|
|||||||
bool default_to_smooth_interp);
|
bool default_to_smooth_interp);
|
||||||
void nir_link_xfb_varyings(nir_shader *producer, nir_shader *consumer);
|
void nir_link_xfb_varyings(nir_shader *producer, nir_shader *consumer);
|
||||||
bool nir_link_opt_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,
|
bool nir_lower_amul(nir_shader *shader,
|
||||||
int (*type_size)(const struct glsl_type *, bool));
|
int (*type_size)(const struct glsl_type *, bool));
|
||||||
|
@@ -1001,6 +1001,71 @@ replace_duplicate_input(nir_shader *shader, nir_variable *input_var,
|
|||||||
return progress;
|
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
|
bool
|
||||||
nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
|
nir_link_opt_varyings(nir_shader *producer, nir_shader *consumer)
|
||||||
{
|
{
|
||||||
|
@@ -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,
|
NIR_PASS_V(consumer, nir_remove_dead_variables, nir_var_shader_in,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nir_link_varying_precision(producer, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Reference in New Issue
Block a user