/* ir_variable::is_unmatched_generic_inout is used by the linker while
* connecting outputs from one stage to inputs of the next stage.
- *
- * There are two implicit assumptions here. First, we assume that any
- * built-in variable (i.e., non-generic in or out) will have
- * explicit_location set. Second, we assume that any generic in or out
- * will not have explicit_location set.
- *
- * This second assumption will only be valid until
- * GL_ARB_separate_shader_objects is supported. When that extension is
- * implemented, this function will need some modifications.
*/
- if (!var->data.explicit_location) {
- var->data.is_unmatched_generic_inout = 1;
- } else {
+ if (var->data.explicit_location &&
+ var->data.location < VARYING_SLOT_VAR0) {
var->data.is_unmatched_generic_inout = 0;
+ } else {
+ var->data.is_unmatched_generic_inout = 1;
}
}
}
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
struct gl_shader *sh = prog->_LinkedShaders[i];
- prog->UniformBlockStageIndex[i] = ralloc_array(prog, int,
- max_num_uniform_blocks);
+ prog->InterfaceBlockStageIndex[i] = ralloc_array(prog, int,
+ max_num_uniform_blocks);
for (unsigned int j = 0; j < max_num_uniform_blocks; j++)
- prog->UniformBlockStageIndex[i][j] = -1;
+ prog->InterfaceBlockStageIndex[i][j] = -1;
if (sh == NULL)
continue;
return false;
}
- prog->UniformBlockStageIndex[i][index] = j;
+ prog->InterfaceBlockStageIndex[i][index] = j;
}
}
continue;
if (var->data.explicit_location) {
+ var->data.is_unmatched_generic_inout = 0;
if ((var->data.location >= (int)(max_index + generic_base))
|| (var->data.location < 0)) {
linker_error(prog,
return false;
}
- const unsigned slots = var->type->count_attribute_slots();
+ const unsigned slots = var->type->count_attribute_slots(target_index == MESA_SHADER_VERTEX ? true : false);
/* If the variable is not a built-in and has a location statically
* assigned in the shader (presumably via a layout qualifier), make sure
* issue (3) of the GL_ARB_vertex_attrib_64bit behavior, this
* is optional behavior, but it seems preferable.
*/
- const glsl_type *type = var->type->without_array();
- if (type == glsl_type::dvec3_type ||
- type == glsl_type::dvec4_type ||
- type == glsl_type::dmat2x3_type ||
- type == glsl_type::dmat2x4_type ||
- type == glsl_type::dmat3_type ||
- type == glsl_type::dmat3x4_type ||
- type == glsl_type::dmat4x3_type ||
- type == glsl_type::dmat4_type) {
+ if (var->type->without_array()->is_dual_slot_double())
double_storage_locations |= (use_mask << attr);
- }
}
continue;
return true;
}
+/**
+ * Match explicit locations of outputs to inputs and deactivate the
+ * unmatch flag if found so we don't optimise them away.
+ */
+static void
+match_explicit_outputs_to_inputs(struct gl_shader_program *prog,
+ gl_shader *producer,
+ gl_shader *consumer)
+{
+ glsl_symbol_table parameters;
+ ir_variable *explicit_locations[MAX_VARYING] = { NULL };
+
+ /* Find all shader outputs in the "producer" stage.
+ */
+ foreach_in_list(ir_instruction, node, producer->ir) {
+ ir_variable *const var = node->as_variable();
+
+ if ((var == NULL) || (var->data.mode != ir_var_shader_out))
+ continue;
+
+ if (var->data.explicit_location &&
+ var->data.location >= VARYING_SLOT_VAR0) {
+ const unsigned idx = var->data.location - VARYING_SLOT_VAR0;
+ if (explicit_locations[idx] == NULL)
+ explicit_locations[idx] = var;
+ }
+ }
+
+ /* Match inputs to outputs */
+ foreach_in_list(ir_instruction, node, consumer->ir) {
+ ir_variable *const input = node->as_variable();
+
+ if ((input == NULL) || (input->data.mode != ir_var_shader_in))
+ continue;
+
+ ir_variable *output = NULL;
+ if (input->data.explicit_location
+ && input->data.location >= VARYING_SLOT_VAR0) {
+ output = explicit_locations[input->data.location - VARYING_SLOT_VAR0];
+
+ if (output != NULL){
+ input->data.is_unmatched_generic_inout = 0;
+ output->data.is_unmatched_generic_inout = 0;
+ }
+ }
+ }
+}
/**
* Demote shader inputs and outputs that are not used in other stages
}
for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) {
- if (prog->UniformBlockStageIndex[j][i] != -1) {
+ if (prog->InterfaceBlockStageIndex[j][i] != -1) {
struct gl_shader *sh = prog->_LinkedShaders[j];
- int stage_index = prog->UniformBlockStageIndex[j][i];
+ int stage_index = prog->InterfaceBlockStageIndex[j][i];
if (sh && sh->BufferInterfaceBlocks[stage_index].IsShaderStorage) {
shader_blocks[j]++;
total_shader_storage_blocks++;
total_image_units += sh->NumImages;
for (unsigned j = 0; j < prog->NumBufferInterfaceBlocks; j++) {
- int stage_index = prog->UniformBlockStageIndex[i][j];
+ int stage_index = prog->InterfaceBlockStageIndex[i][j];
if (stage_index != -1 && sh->BufferInterfaceBlocks[stage_index].IsShaderStorage)
total_shader_storage_blocks++;
}
foreach_in_list(ir_instruction, node, sh->ir) {
ir_variable *var = node->as_variable();
if (var && var->data.mode == ir_var_shader_out)
- fragment_outputs += var->type->count_attribute_slots();
+ /* since there are no double fs outputs - pass false */
+ fragment_outputs += var->type->count_attribute_slots(false);
}
}
}
int block_index = shProg->UniformStorage[i].block_index;
if (block_index != -1) {
for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) {
- if (shProg->UniformBlockStageIndex[j][block_index] != -1)
+ if (shProg->InterfaceBlockStageIndex[j][block_index] != -1)
stageref |= (1 << j);
}
}
continue;
for (int j = MESA_SHADER_VERTEX; j < MESA_SHADER_STAGES; j++) {
- if (!shProg->UniformStorage[i].opaque[j].active)
+ if (!shProg->UniformStorage[i].opaque[j].active ||
+ !shProg->UniformStorage[i].type->is_subroutine())
continue;
type = _mesa_shader_stage_to_subroutine_uniform((gl_shader_stage)j);
return;
}
}
-
- /* TODO - following extensions will require more resource types:
- *
- * GL_ARB_shader_storage_buffer_object
- */
}
/**
sh->SubroutineFunctions[sh->NumSubroutineFunctions].types =
ralloc_array(sh, const struct glsl_type *,
fn->num_subroutine_types);
+
+ /* From Section 4.4.4(Subroutine Function Layout Qualifiers) of the
+ * GLSL 4.5 spec:
+ *
+ * "Each subroutine with an index qualifier in the shader must be
+ * given a unique index, otherwise a compile or link error will be
+ * generated."
+ */
+ for (unsigned j = 0; j < sh->NumSubroutineFunctions; j++) {
+ if (sh->SubroutineFunctions[j].index != -1 &&
+ sh->SubroutineFunctions[j].index == fn->subroutine_index) {
+ linker_error(prog, "each subroutine index qualifier in the "
+ "shader must be unique\n");
+ return;
+ }
+ }
+ sh->SubroutineFunctions[sh->NumSubroutineFunctions].index =
+ fn->subroutine_index;
+
for (int j = 0; j < fn->num_subroutine_types; j++)
sh->SubroutineFunctions[sh->NumSubroutineFunctions].types[j] = fn->subroutine_types[j];
sh->NumSubroutineFunctions++;
}
+
+ /* Assign index for subroutines without an explicit index*/
+ int index = 0;
+ for (unsigned j = 0; j < sh->NumSubroutineFunctions; j++) {
+ while (sh->SubroutineFunctions[j].index == -1) {
+ for (unsigned k = 0; k < sh->NumSubroutineFunctions; k++) {
+ if (sh->SubroutineFunctions[k].index == index)
+ break;
+ else if (k == sh->NumSubroutineFunctions - 1)
+ sh->SubroutineFunctions[j].index = index;
+ }
+ index++;
+ }
+ }
}
}
assert(*num_ubos + *num_ssbos == num_blocks);
}
+static void
+set_always_active_io(exec_list *ir, ir_variable_mode io_mode)
+{
+ assert(io_mode == ir_var_shader_in || io_mode == ir_var_shader_out);
+
+ foreach_in_list(ir_instruction, node, ir) {
+ ir_variable *const var = node->as_variable();
+
+ if (var == NULL || var->data.mode != io_mode)
+ continue;
+
+ /* Don't set always active on builtins that haven't been redeclared */
+ if (var->data.how_declared == ir_var_declared_implicitly)
+ continue;
+
+ var->data.always_active_io = true;
+ }
+}
+
+/**
+ * When separate shader programs are enabled, only input/outputs between
+ * the stages of a multi-stage separate program can be safely removed
+ * from the shader interface. Other inputs/outputs must remain active.
+ */
+static void
+disable_varying_optimizations_for_sso(struct gl_shader_program *prog)
+{
+ unsigned first, last;
+ assert(prog->SeparateShader);
+
+ first = MESA_SHADER_STAGES;
+ last = 0;
+
+ /* Determine first and last stage. Excluding the compute stage */
+ for (unsigned i = 0; i < MESA_SHADER_COMPUTE; i++) {
+ if (!prog->_LinkedShaders[i])
+ continue;
+ if (first == MESA_SHADER_STAGES)
+ first = i;
+ last = i;
+ }
+
+ if (first == MESA_SHADER_STAGES)
+ return;
+
+ for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+ gl_shader *sh = prog->_LinkedShaders[stage];
+ if (!sh)
+ continue;
+
+ if (first == last) {
+ /* For a single shader program only allow inputs to the vertex shader
+ * and outputs from the fragment shader to be removed.
+ */
+ if (stage != MESA_SHADER_VERTEX)
+ set_always_active_io(sh->ir, ir_var_shader_in);
+ if (stage != MESA_SHADER_FRAGMENT)
+ set_always_active_io(sh->ir, ir_var_shader_out);
+ } else {
+ /* For multi-stage separate shader programs only allow inputs and
+ * outputs between the shader stages to be removed as well as inputs
+ * to the vertex shader and outputs from the fragment shader.
+ */
+ if (stage == first && stage != MESA_SHADER_VERTEX)
+ set_always_active_io(sh->ir, ir_var_shader_in);
+ else if (stage == last && stage != MESA_SHADER_FRAGMENT)
+ set_always_active_io(sh->ir, ir_var_shader_out);
+ }
+ }
+}
+
void
link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
{
if (!prog->LinkStatus)
goto done;
- unsigned prev;
+ unsigned first, last, prev;
- for (prev = 0; prev <= MESA_SHADER_FRAGMENT; prev++) {
- if (prog->_LinkedShaders[prev] != NULL)
- break;
+ first = MESA_SHADER_STAGES;
+ last = 0;
+
+ /* Determine first and last stage. */
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (!prog->_LinkedShaders[i])
+ continue;
+ if (first == MESA_SHADER_STAGES)
+ first = i;
+ last = i;
}
check_explicit_uniform_locations(ctx, prog);
/* Validate the inputs of each stage with the output of the preceding
* stage.
*/
+ prev = first;
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
if (prog->_LinkedShaders[i] == NULL)
continue;
}
}
+ if (prog->SeparateShader)
+ disable_varying_optimizations_for_sso(prog);
+
if (!interstage_cross_validate_uniform_blocks(prog))
goto done;
}
}
+ prev = first;
+ for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ match_explicit_outputs_to_inputs(prog, prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ prev = i;
+ }
+
if (!assign_attribute_or_color_locations(prog, &ctx->Const,
MESA_SHADER_VERTEX)) {
goto done;
goto done;
}
- unsigned first, last;
-
- first = MESA_SHADER_STAGES;
- last = 0;
-
- /* Determine first and last stage. */
- for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
- if (!prog->_LinkedShaders[i])
- continue;
- if (first == MESA_SHADER_STAGES)
- first = i;
- last = i;
- }
-
if (num_tfeedback_decls != 0) {
/* From GL_EXT_transform_feedback:
* A program will fail to link if:
if (first < MESA_SHADER_FRAGMENT) {
gl_shader *const sh = prog->_LinkedShaders[last];
- if (first == MESA_SHADER_GEOMETRY) {
+ if (first != MESA_SHADER_VERTEX) {
/* There was no vertex shader, but we still have to assign varying
- * locations for use by geometry shader inputs in SSO.
+ * locations for use by tessellation/geometry shader inputs in SSO.
*
* If the shader is not separable (i.e., prog->SeparateShader is
- * false), linking will have already failed when first is
- * MESA_SHADER_GEOMETRY.
+ * 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],
do_dead_builtin_varyings(ctx, sh, NULL,
num_tfeedback_decls, tfeedback_decls);
- if (!prog->SeparateShader)
+ if (!prog->SeparateShader) {
demote_shader_inputs_and_outputs(sh, ir_var_shader_out);
-
- /* Eliminate code that is now dead due to unused outputs being demoted.
- */
- while (do_dead_code(sh->ir, false))
- ;
+ /* Eliminate code that is now dead due to unused outputs being
+ * demoted.
+ */
+ while (do_dead_code(sh->ir, false))
+ ;
+ }
}
else if (first == MESA_SHADER_FRAGMENT) {
/* If the program only contains a fragment shader...
0 /* num_tfeedback_decls */,
NULL /* tfeedback_decls */))
goto done;
- } else
+ } else {
demote_shader_inputs_and_outputs(sh, ir_var_shader_in);
-
- while (do_dead_code(sh->ir, false))
- ;
+ /* Eliminate code that is now dead due to unused inputs being
+ * demoted.
+ */
+ while (do_dead_code(sh->ir, false))
+ ;
+ }
}
next = last;
/* FINISHME: Assign fragment shader output locations. */
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ if (ctx->Const.ShaderCompilerOptions[i].LowerBufferInterfaceBlocks)
+ lower_ubo_reference(prog->_LinkedShaders[i]);
+
+ if (ctx->Const.ShaderCompilerOptions[i].LowerShaderSharedVariables)
+ lower_shared_reference(prog->_LinkedShaders[i],
+ &prog->Comp.SharedSize);
+
+ lower_vector_derefs(prog->_LinkedShaders[i]);
+ }
+
done:
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
free(shader_list[i]);