glsl: clean up and fix bug in varying linking rules
The existing code was very hard to follow and has been the source of at least 3 bugs in the past year. The existing code also has a bug for SSO where if we have a multi-stage SSO for example a tes -> gs program, if we try to use transform feedback with gs the existing code would look for the transform feedback varyings in the tes stage and fail as it can't find them. V2: Add more code comments, always try to remove unused inputs to the first stage. Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
@@ -4462,93 +4462,82 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Linking the stages in the opposite order (from fragment to vertex)
|
/* If there is no fragment shader we need to set transform feedback.
|
||||||
* ensures that inter-shader outputs written to in an earlier stage are
|
*
|
||||||
* eliminated if they are (transitively) not used in a later stage.
|
* For SSO we need also need to assign output locations, we assign them
|
||||||
|
* here because we need to do it for both single stage programs and multi
|
||||||
|
* stage programs.
|
||||||
*/
|
*/
|
||||||
int next;
|
if (last < MESA_SHADER_FRAGMENT &&
|
||||||
|
(num_tfeedback_decls != 0 || prog->SeparateShader)) {
|
||||||
|
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||||
|
prog->_LinkedShaders[last], NULL,
|
||||||
|
num_tfeedback_decls, tfeedback_decls))
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (first < MESA_SHADER_FRAGMENT) {
|
if (last <= MESA_SHADER_FRAGMENT) {
|
||||||
gl_shader *const sh = prog->_LinkedShaders[last];
|
/* Remove unused varyings from the first/last stage unless SSO */
|
||||||
|
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||||
if (first != MESA_SHADER_VERTEX) {
|
prog->_LinkedShaders[first],
|
||||||
/* There was no vertex shader, but we still have to assign varying
|
ir_var_shader_in);
|
||||||
* locations for use by tessellation/geometry shader inputs in SSO.
|
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||||
*
|
prog->_LinkedShaders[last],
|
||||||
* If the shader is not separable (i.e., prog->SeparateShader is
|
|
||||||
* false), linking will have already failed when first is not
|
|
||||||
* MESA_SHADER_VERTEX.
|
|
||||||
*/
|
|
||||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
|
||||||
NULL, prog->_LinkedShaders[first],
|
|
||||||
num_tfeedback_decls, tfeedback_decls))
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last != MESA_SHADER_FRAGMENT &&
|
|
||||||
(num_tfeedback_decls != 0 || prog->SeparateShader)) {
|
|
||||||
/* There was no fragment shader, but we still have to assign varying
|
|
||||||
* locations for use by transform feedback.
|
|
||||||
*/
|
|
||||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
|
||||||
sh, NULL,
|
|
||||||
num_tfeedback_decls, tfeedback_decls))
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_dead_builtin_varyings(ctx, sh, NULL,
|
|
||||||
num_tfeedback_decls, tfeedback_decls);
|
|
||||||
|
|
||||||
remove_unused_shader_inputs_and_outputs(prog->SeparateShader, sh,
|
|
||||||
ir_var_shader_out);
|
ir_var_shader_out);
|
||||||
}
|
|
||||||
else if (first == MESA_SHADER_FRAGMENT) {
|
|
||||||
/* If the program only contains a fragment shader...
|
|
||||||
*/
|
|
||||||
gl_shader *const sh = prog->_LinkedShaders[first];
|
|
||||||
|
|
||||||
do_dead_builtin_varyings(ctx, NULL, sh,
|
/* If the program is made up of only a single stage */
|
||||||
num_tfeedback_decls, tfeedback_decls);
|
if (first == last) {
|
||||||
|
|
||||||
if (prog->SeparateShader) {
|
gl_shader *const sh = prog->_LinkedShaders[last];
|
||||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
if (prog->SeparateShader) {
|
||||||
NULL /* producer */,
|
/* Assign input locations for SSO, output locations are already
|
||||||
sh /* consumer */,
|
* assigned.
|
||||||
0 /* num_tfeedback_decls */,
|
*/
|
||||||
NULL /* tfeedback_decls */))
|
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||||
goto done;
|
NULL /* producer */,
|
||||||
|
sh /* consumer */,
|
||||||
|
0 /* num_tfeedback_decls */,
|
||||||
|
NULL /* tfeedback_decls */))
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_dead_builtin_varyings(ctx, NULL, sh, 0, NULL);
|
||||||
|
do_dead_builtin_varyings(ctx, sh, NULL, num_tfeedback_decls,
|
||||||
|
tfeedback_decls);
|
||||||
} else {
|
} else {
|
||||||
remove_unused_shader_inputs_and_outputs(false, sh,
|
/* Linking the stages in the opposite order (from fragment to vertex)
|
||||||
ir_var_shader_in);
|
* ensures that inter-shader outputs written to in an earlier stage
|
||||||
|
* are eliminated if they are (transitively) not used in a later
|
||||||
|
* stage.
|
||||||
|
*/
|
||||||
|
int next = last;
|
||||||
|
for (int i = next - 1; i >= 0; i--) {
|
||||||
|
if (prog->_LinkedShaders[i] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gl_shader *const sh_i = prog->_LinkedShaders[i];
|
||||||
|
gl_shader *const sh_next = prog->_LinkedShaders[next];
|
||||||
|
|
||||||
|
if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next,
|
||||||
|
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
||||||
|
tfeedback_decls))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
do_dead_builtin_varyings(ctx, sh_i, sh_next,
|
||||||
|
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
||||||
|
tfeedback_decls);
|
||||||
|
|
||||||
|
/* This must be done after all dead varyings are eliminated. */
|
||||||
|
if (!check_against_output_limit(ctx, prog, sh_i))
|
||||||
|
goto done;
|
||||||
|
if (!check_against_input_limit(ctx, prog, sh_next))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
next = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next = last;
|
|
||||||
for (int i = next - 1; i >= 0; i--) {
|
|
||||||
if (prog->_LinkedShaders[i] == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
gl_shader *const sh_i = prog->_LinkedShaders[i];
|
|
||||||
gl_shader *const sh_next = prog->_LinkedShaders[next];
|
|
||||||
|
|
||||||
if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next,
|
|
||||||
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
|
||||||
tfeedback_decls))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
do_dead_builtin_varyings(ctx, sh_i, sh_next,
|
|
||||||
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
|
||||||
tfeedback_decls);
|
|
||||||
|
|
||||||
/* This must be done after all dead varyings are eliminated. */
|
|
||||||
if (!check_against_output_limit(ctx, prog, sh_i))
|
|
||||||
goto done;
|
|
||||||
if (!check_against_input_limit(ctx, prog, sh_next))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
next = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls))
|
if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user