From 588702cc41122607c8bf8a589b0714546bac190c Mon Sep 17 00:00:00 2001 From: Timothy Arceri Date: Wed, 27 Jul 2016 15:20:42 +1000 Subject: [PATCH] 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 --- src/compiler/glsl/linker.cpp | 429 ++++++++++++++++++----------------- 1 file changed, 222 insertions(+), 207 deletions(-) diff --git a/src/compiler/glsl/linker.cpp b/src/compiler/glsl/linker.cpp index ceb86aa0a92..569a8b9f947 100644 --- a/src/compiler/glsl/linker.cpp +++ b/src/compiler/glsl/linker.cpp @@ -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 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 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]); -- 2.30.2