glsl: split out varying and uniform linking code
Here a new function link_varyings_and_uniforms() is created this should help make it easier to follow the code in link_shader() which was getting very large. Note the end of the new function contains a for loop with some lowering calls that currently don't seem related to varyings or uniforms but they are a dependancy for converting to NIR ealier so we move things here now to keep things easy to follow. Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
+222
-207
@@ -4477,6 +4477,226 @@ disable_varying_optimizations_for_sso(struct gl_shader_program *prog)
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
link_varyings_and_uniforms(unsigned first, unsigned last,
|
||||
unsigned num_explicit_uniform_locs,
|
||||
struct gl_context *ctx,
|
||||
struct gl_shader_program *prog, void *mem_ctx)
|
||||
{
|
||||
bool has_xfb_qualifiers = false;
|
||||
unsigned num_tfeedback_decls = 0;
|
||||
char **varying_names = NULL;
|
||||
tfeedback_decl *tfeedback_decls = NULL;
|
||||
|
||||
/* Mark all generic shader inputs and outputs as unpaired. */
|
||||
for (unsigned i = MESA_SHADER_VERTEX; i <= MESA_SHADER_FRAGMENT; i++) {
|
||||
if (prog->_LinkedShaders[i] != NULL) {
|
||||
link_invalidate_variable_locations(prog->_LinkedShaders[i]->ir);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prev = first;
|
||||
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
|
||||
if (prog->_LinkedShaders[i] == NULL)
|
||||
continue;
|
||||
|
||||
match_explicit_outputs_to_inputs(prog->_LinkedShaders[prev],
|
||||
prog->_LinkedShaders[i]);
|
||||
prev = i;
|
||||
}
|
||||
|
||||
if (!assign_attribute_or_color_locations(prog, &ctx->Const,
|
||||
MESA_SHADER_VERTEX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!assign_attribute_or_color_locations(prog, &ctx->Const,
|
||||
MESA_SHADER_FRAGMENT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* From the ARB_enhanced_layouts spec:
|
||||
*
|
||||
* "If the shader used to record output variables for transform feedback
|
||||
* varyings uses the "xfb_buffer", "xfb_offset", or "xfb_stride" layout
|
||||
* qualifiers, the values specified by TransformFeedbackVaryings are
|
||||
* ignored, and the set of variables captured for transform feedback is
|
||||
* instead derived from the specified layout qualifiers."
|
||||
*/
|
||||
for (int i = MESA_SHADER_FRAGMENT - 1; i >= 0; i--) {
|
||||
/* Find last stage before fragment shader */
|
||||
if (prog->_LinkedShaders[i]) {
|
||||
has_xfb_qualifiers =
|
||||
process_xfb_layout_qualifiers(mem_ctx, prog->_LinkedShaders[i],
|
||||
&num_tfeedback_decls,
|
||||
&varying_names);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_xfb_qualifiers) {
|
||||
num_tfeedback_decls = prog->TransformFeedback.NumVarying;
|
||||
varying_names = prog->TransformFeedback.VaryingNames;
|
||||
}
|
||||
|
||||
if (num_tfeedback_decls != 0) {
|
||||
/* From GL_EXT_transform_feedback:
|
||||
* A program will fail to link if:
|
||||
*
|
||||
* * the <count> specified by TransformFeedbackVaryingsEXT is
|
||||
* non-zero, but the program object has no vertex or geometry
|
||||
* shader;
|
||||
*/
|
||||
if (first >= MESA_SHADER_FRAGMENT) {
|
||||
linker_error(prog, "Transform feedback varyings specified, but "
|
||||
"no vertex, tessellation, or geometry shader is "
|
||||
"present.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl,
|
||||
num_tfeedback_decls);
|
||||
if (!parse_tfeedback_decls(ctx, prog, mem_ctx, num_tfeedback_decls,
|
||||
varying_names, tfeedback_decls))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If there is no fragment shader we need to set transform feedback.
|
||||
*
|
||||
* For SSO we 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.
|
||||
*/
|
||||
if (last < MESA_SHADER_FRAGMENT &&
|
||||
(num_tfeedback_decls != 0 || prog->SeparateShader)) {
|
||||
const uint64_t reserved_out_slots =
|
||||
reserved_varying_slot(prog->_LinkedShaders[last], ir_var_shader_out);
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||
prog->_LinkedShaders[last], NULL,
|
||||
num_tfeedback_decls, tfeedback_decls,
|
||||
reserved_out_slots))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (last <= MESA_SHADER_FRAGMENT) {
|
||||
/* Remove unused varyings from the first/last stage unless SSO */
|
||||
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||
prog->_LinkedShaders[first],
|
||||
ir_var_shader_in);
|
||||
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||
prog->_LinkedShaders[last],
|
||||
ir_var_shader_out);
|
||||
|
||||
/* If the program is made up of only a single stage */
|
||||
if (first == last) {
|
||||
|
||||
gl_linked_shader *const sh = prog->_LinkedShaders[last];
|
||||
if (prog->SeparateShader) {
|
||||
const uint64_t reserved_slots =
|
||||
reserved_varying_slot(sh, ir_var_shader_in);
|
||||
|
||||
/* Assign input locations for SSO, output locations are already
|
||||
* assigned.
|
||||
*/
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||
NULL /* producer */,
|
||||
sh /* consumer */,
|
||||
0 /* num_tfeedback_decls */,
|
||||
NULL /* tfeedback_decls */,
|
||||
reserved_slots))
|
||||
return false;
|
||||
}
|
||||
|
||||
do_dead_builtin_varyings(ctx, NULL, sh, 0, NULL);
|
||||
do_dead_builtin_varyings(ctx, sh, NULL, num_tfeedback_decls,
|
||||
tfeedback_decls);
|
||||
} else {
|
||||
/* Linking the stages in the opposite order (from fragment to vertex)
|
||||
* 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 && i != 0)
|
||||
continue;
|
||||
|
||||
gl_linked_shader *const sh_i = prog->_LinkedShaders[i];
|
||||
gl_linked_shader *const sh_next = prog->_LinkedShaders[next];
|
||||
|
||||
const uint64_t reserved_out_slots =
|
||||
reserved_varying_slot(sh_i, ir_var_shader_out);
|
||||
const uint64_t reserved_in_slots =
|
||||
reserved_varying_slot(sh_next, ir_var_shader_in);
|
||||
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next,
|
||||
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
||||
tfeedback_decls,
|
||||
reserved_out_slots | reserved_in_slots))
|
||||
return false;
|
||||
|
||||
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 (sh_i != NULL) {
|
||||
unsigned slots_used = _mesa_bitcount_64(reserved_out_slots);
|
||||
if (!check_against_output_limit(ctx, prog, sh_i, slots_used)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned slots_used = _mesa_bitcount_64(reserved_in_slots);
|
||||
if (!check_against_input_limit(ctx, prog, sh_next, slots_used))
|
||||
return false;
|
||||
|
||||
next = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls,
|
||||
has_xfb_qualifiers))
|
||||
return false;
|
||||
|
||||
update_array_sizes(prog);
|
||||
link_assign_uniform_locations(prog, ctx->Const.UniformBooleanTrue,
|
||||
num_explicit_uniform_locs,
|
||||
ctx->Const.MaxUserAssignableUniformLocations);
|
||||
link_assign_atomic_counter_resources(ctx, prog);
|
||||
|
||||
link_calculate_subroutine_compat(prog);
|
||||
check_resources(ctx, prog);
|
||||
check_subroutine_resources(prog);
|
||||
check_image_resources(ctx, prog);
|
||||
link_check_atomic_counter_resources(ctx, prog);
|
||||
|
||||
if (!prog->LinkStatus)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
||||
if (prog->_LinkedShaders[i] == NULL)
|
||||
continue;
|
||||
|
||||
const struct gl_shader_compiler_options *options =
|
||||
&ctx->Const.ShaderCompilerOptions[i];
|
||||
|
||||
if (options->LowerBufferInterfaceBlocks)
|
||||
lower_ubo_reference(prog->_LinkedShaders[i],
|
||||
options->ClampBlockIndicesToArrayBounds);
|
||||
|
||||
if (options->LowerShaderSharedVariables)
|
||||
lower_shared_reference(prog->_LinkedShaders[i],
|
||||
&prog->Comp.SharedSize);
|
||||
|
||||
lower_vector_derefs(prog->_LinkedShaders[i]);
|
||||
do_vec_index_to_swizzle(prog->_LinkedShaders[i]->ir);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
||||
{
|
||||
@@ -4503,11 +4723,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned num_tfeedback_decls = 0;
|
||||
unsigned int num_explicit_uniform_locs = 0;
|
||||
bool has_xfb_qualifiers = false;
|
||||
char **varying_names = NULL;
|
||||
tfeedback_decl *tfeedback_decls = NULL;
|
||||
|
||||
void *mem_ctx = ralloc_context(NULL); // temporary linker context
|
||||
|
||||
@@ -4801,192 +5017,10 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
||||
/* Check and validate stream emissions in geometry shaders */
|
||||
validate_geometry_shader_emissions(ctx, prog);
|
||||
|
||||
/* Mark all generic shader inputs and outputs as unpaired. */
|
||||
for (unsigned i = MESA_SHADER_VERTEX; i <= MESA_SHADER_FRAGMENT; i++) {
|
||||
if (prog->_LinkedShaders[i] != NULL) {
|
||||
link_invalidate_variable_locations(prog->_LinkedShaders[i]->ir);
|
||||
}
|
||||
}
|
||||
|
||||
prev = first;
|
||||
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
|
||||
if (prog->_LinkedShaders[i] == NULL)
|
||||
continue;
|
||||
|
||||
match_explicit_outputs_to_inputs(prog->_LinkedShaders[prev],
|
||||
prog->_LinkedShaders[i]);
|
||||
prev = i;
|
||||
}
|
||||
|
||||
if (!assign_attribute_or_color_locations(prog, &ctx->Const,
|
||||
MESA_SHADER_VERTEX)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!assign_attribute_or_color_locations(prog, &ctx->Const,
|
||||
MESA_SHADER_FRAGMENT)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* From the ARB_enhanced_layouts spec:
|
||||
*
|
||||
* "If the shader used to record output variables for transform feedback
|
||||
* varyings uses the "xfb_buffer", "xfb_offset", or "xfb_stride" layout
|
||||
* qualifiers, the values specified by TransformFeedbackVaryings are
|
||||
* ignored, and the set of variables captured for transform feedback is
|
||||
* instead derived from the specified layout qualifiers."
|
||||
*/
|
||||
for (int i = MESA_SHADER_FRAGMENT - 1; i >= 0; i--) {
|
||||
/* Find last stage before fragment shader */
|
||||
if (prog->_LinkedShaders[i]) {
|
||||
has_xfb_qualifiers =
|
||||
process_xfb_layout_qualifiers(mem_ctx, prog->_LinkedShaders[i],
|
||||
&num_tfeedback_decls,
|
||||
&varying_names);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_xfb_qualifiers) {
|
||||
num_tfeedback_decls = prog->TransformFeedback.NumVarying;
|
||||
varying_names = prog->TransformFeedback.VaryingNames;
|
||||
}
|
||||
|
||||
if (num_tfeedback_decls != 0) {
|
||||
/* From GL_EXT_transform_feedback:
|
||||
* A program will fail to link if:
|
||||
*
|
||||
* * the <count> specified by TransformFeedbackVaryingsEXT is
|
||||
* non-zero, but the program object has no vertex or geometry
|
||||
* shader;
|
||||
*/
|
||||
if (first >= MESA_SHADER_FRAGMENT) {
|
||||
linker_error(prog, "Transform feedback varyings specified, but "
|
||||
"no vertex, tessellation, or geometry shader is "
|
||||
"present.\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
tfeedback_decls = ralloc_array(mem_ctx, tfeedback_decl,
|
||||
num_tfeedback_decls);
|
||||
if (!parse_tfeedback_decls(ctx, prog, mem_ctx, num_tfeedback_decls,
|
||||
varying_names, tfeedback_decls))
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If there is no fragment shader we need to set transform feedback.
|
||||
*
|
||||
* For SSO we 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.
|
||||
*/
|
||||
if (last < MESA_SHADER_FRAGMENT &&
|
||||
(num_tfeedback_decls != 0 || prog->SeparateShader)) {
|
||||
const uint64_t reserved_out_slots =
|
||||
reserved_varying_slot(prog->_LinkedShaders[last], ir_var_shader_out);
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||
prog->_LinkedShaders[last], NULL,
|
||||
num_tfeedback_decls, tfeedback_decls,
|
||||
reserved_out_slots))
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (last <= MESA_SHADER_FRAGMENT) {
|
||||
/* Remove unused varyings from the first/last stage unless SSO */
|
||||
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||
prog->_LinkedShaders[first],
|
||||
ir_var_shader_in);
|
||||
remove_unused_shader_inputs_and_outputs(prog->SeparateShader,
|
||||
prog->_LinkedShaders[last],
|
||||
ir_var_shader_out);
|
||||
|
||||
/* If the program is made up of only a single stage */
|
||||
if (first == last) {
|
||||
|
||||
gl_linked_shader *const sh = prog->_LinkedShaders[last];
|
||||
if (prog->SeparateShader) {
|
||||
const uint64_t reserved_slots =
|
||||
reserved_varying_slot(sh, ir_var_shader_in);
|
||||
|
||||
/* Assign input locations for SSO, output locations are already
|
||||
* assigned.
|
||||
*/
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog,
|
||||
NULL /* producer */,
|
||||
sh /* consumer */,
|
||||
0 /* num_tfeedback_decls */,
|
||||
NULL /* tfeedback_decls */,
|
||||
reserved_slots))
|
||||
goto done;
|
||||
}
|
||||
|
||||
do_dead_builtin_varyings(ctx, NULL, sh, 0, NULL);
|
||||
do_dead_builtin_varyings(ctx, sh, NULL, num_tfeedback_decls,
|
||||
tfeedback_decls);
|
||||
} else {
|
||||
/* Linking the stages in the opposite order (from fragment to vertex)
|
||||
* 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 && i != 0)
|
||||
continue;
|
||||
|
||||
gl_linked_shader *const sh_i = prog->_LinkedShaders[i];
|
||||
gl_linked_shader *const sh_next = prog->_LinkedShaders[next];
|
||||
|
||||
const uint64_t reserved_out_slots =
|
||||
reserved_varying_slot(sh_i, ir_var_shader_out);
|
||||
const uint64_t reserved_in_slots =
|
||||
reserved_varying_slot(sh_next, ir_var_shader_in);
|
||||
|
||||
if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next,
|
||||
next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
|
||||
tfeedback_decls,
|
||||
reserved_out_slots | reserved_in_slots))
|
||||
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 (sh_i != NULL) {
|
||||
unsigned slots_used = _mesa_bitcount_64(reserved_out_slots);
|
||||
if (!check_against_output_limit(ctx, prog, sh_i, slots_used)) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned slots_used = _mesa_bitcount_64(reserved_in_slots);
|
||||
if (!check_against_input_limit(ctx, prog, sh_next, slots_used))
|
||||
goto done;
|
||||
|
||||
next = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls,
|
||||
has_xfb_qualifiers))
|
||||
goto done;
|
||||
|
||||
update_array_sizes(prog);
|
||||
link_assign_uniform_locations(prog, ctx->Const.UniformBooleanTrue,
|
||||
num_explicit_uniform_locs,
|
||||
ctx->Const.MaxUserAssignableUniformLocations);
|
||||
link_assign_atomic_counter_resources(ctx, prog);
|
||||
store_fragdepth_layout(prog);
|
||||
|
||||
link_calculate_subroutine_compat(prog);
|
||||
check_resources(ctx, prog);
|
||||
check_subroutine_resources(prog);
|
||||
check_image_resources(ctx, prog);
|
||||
link_check_atomic_counter_resources(ctx, prog);
|
||||
|
||||
if (!prog->LinkStatus)
|
||||
if(!link_varyings_and_uniforms(first, last, num_explicit_uniform_locs, ctx,
|
||||
prog, mem_ctx))
|
||||
goto done;
|
||||
|
||||
/* OpenGL ES < 3.1 requires that a vertex shader and a fragment shader both
|
||||
@@ -5024,25 +5058,6 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
||||
if (prog->_LinkedShaders[i] == NULL)
|
||||
continue;
|
||||
|
||||
const struct gl_shader_compiler_options *options =
|
||||
&ctx->Const.ShaderCompilerOptions[i];
|
||||
|
||||
if (options->LowerBufferInterfaceBlocks)
|
||||
lower_ubo_reference(prog->_LinkedShaders[i],
|
||||
options->ClampBlockIndicesToArrayBounds);
|
||||
|
||||
if (options->LowerShaderSharedVariables)
|
||||
lower_shared_reference(prog->_LinkedShaders[i],
|
||||
&prog->Comp.SharedSize);
|
||||
|
||||
lower_vector_derefs(prog->_LinkedShaders[i]);
|
||||
do_vec_index_to_swizzle(prog->_LinkedShaders[i]->ir);
|
||||
}
|
||||
|
||||
done:
|
||||
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
||||
free(shader_list[i]);
|
||||
|
||||
Reference in New Issue
Block a user