}
}
-bool
+static bool
process_xfb_layout_qualifiers(void *mem_ctx, const gl_linked_shader *sh,
+ struct gl_shader_program *prog,
unsigned *num_tfeedback_decls,
char ***varying_names)
{
* xfb_stride to interface block members so this will catch that case also.
*/
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
- if (sh->info.TransformFeedback.BufferStride[j]) {
+ if (prog->TransformFeedback.BufferStride[j]) {
has_xfb_qualifiers = true;
+ break;
}
}
return has_xfb_qualifiers;
}
-static bool
-anonymous_struct_type_matches(const glsl_type *output_type,
- const glsl_type *to_match)
-{
- while (output_type->is_array() && to_match->is_array()) {
- /* if the lengths at each level don't match fail. */
- if (output_type->length != to_match->length)
- return false;
- output_type = output_type->fields.array;
- to_match = to_match->fields.array;
- }
-
- if (output_type->is_array() || to_match->is_array())
- return false;
- return output_type->is_anonymous() &&
- to_match->is_anonymous() &&
- to_match->record_compare(output_type);
-}
-
/**
* Validate the types and qualifiers of an output from one stage against the
* matching input to another stage.
* fragment language."
*/
if (!output->type->is_array() || !is_gl_identifier(output->name)) {
- bool anon_matches = anonymous_struct_type_matches(output->type, type_to_match);
-
- if (!anon_matches) {
- linker_error(prog,
- "%s shader output `%s' declared as type `%s', "
- "but %s shader input declared as type `%s'\n",
- _mesa_shader_stage_to_string(producer_stage),
- output->name,
- output->type->name,
- _mesa_shader_stage_to_string(consumer_stage),
- input->type->name);
- return;
- }
+ linker_error(prog,
+ "%s shader output `%s' declared as type `%s', "
+ "but %s shader input declared as type `%s'\n",
+ _mesa_shader_stage_to_string(producer_stage),
+ output->name,
+ output->type->name,
+ _mesa_shader_stage_to_string(consumer_stage),
+ input->type->name);
+ return;
}
}
* OpenGLES 3.0 drivers, so we relax the checking in all cases.
*/
if (false /* always skip the centroid check */ &&
- prog->Version < (prog->IsES ? 310 : 430) &&
+ prog->data->Version < (prog->IsES ? 310 : 430) &&
input->data.centroid != output->data.centroid) {
linker_error(prog,
"%s shader output `%s' %s centroid qualifier, "
* and fragment shaders must match."
*/
if (input->data.invariant != output->data.invariant &&
- prog->Version < (prog->IsES ? 300 : 430)) {
+ prog->data->Version < (prog->IsES ? 300 : 430)) {
linker_error(prog,
"%s shader output `%s' %s invariant qualifier, "
"but %s shader input %s invariant qualifier\n",
* "It is a link-time error if, within the same stage, the interpolation
* qualifiers of variables of the same name do not match.
*
+ * Section 4.3.9 (Interpolation) of the GLSL ES 3.00 spec says:
+ *
+ * "When no interpolation qualifier is present, smooth interpolation
+ * is used."
+ *
+ * So we match variables where one is smooth and the other has no explicit
+ * qualifier.
*/
- if (input->data.interpolation != output->data.interpolation &&
- prog->Version < 440) {
+ unsigned input_interpolation = input->data.interpolation;
+ unsigned output_interpolation = output->data.interpolation;
+ if (prog->IsES) {
+ if (input_interpolation == INTERP_MODE_NONE)
+ input_interpolation = INTERP_MODE_SMOOTH;
+ if (output_interpolation == INTERP_MODE_NONE)
+ output_interpolation = INTERP_MODE_SMOOTH;
+ }
+ if (input_interpolation != output_interpolation &&
+ prog->data->Version < 440) {
linker_error(prog,
"%s shader output `%s' specifies %s "
"interpolation qualifier, "
consumer_stage, producer_stage);
}
+static unsigned
+compute_variable_location_slot(ir_variable *var, gl_shader_stage stage)
+{
+ unsigned location_start = VARYING_SLOT_VAR0;
+
+ switch (stage) {
+ case MESA_SHADER_VERTEX:
+ if (var->data.mode == ir_var_shader_in)
+ location_start = VERT_ATTRIB_GENERIC0;
+ break;
+ case MESA_SHADER_TESS_CTRL:
+ case MESA_SHADER_TESS_EVAL:
+ if (var->data.patch)
+ location_start = VARYING_SLOT_PATCH0;
+ break;
+ case MESA_SHADER_FRAGMENT:
+ if (var->data.mode == ir_var_shader_out)
+ location_start = FRAG_RESULT_DATA0;
+ break;
+ default:
+ break;
+ }
+
+ return var->data.location - location_start;
+}
+
+struct explicit_location_info {
+ ir_variable *var;
+ unsigned base_type;
+};
+
+static bool
+check_location_aliasing(struct explicit_location_info explicit_locations[][4],
+ ir_variable *var,
+ unsigned location,
+ unsigned component,
+ unsigned location_limit,
+ const glsl_type *type,
+ gl_shader_program *prog,
+ gl_shader_stage stage)
+{
+ unsigned last_comp;
+ if (type->without_array()->is_record()) {
+ /* The component qualifier can't be used on structs so just treat
+ * all component slots as used.
+ */
+ last_comp = 4;
+ } else {
+ unsigned dmul = type->without_array()->is_64bit() ? 2 : 1;
+ last_comp = component + type->without_array()->vector_elements * dmul;
+ }
+
+ while (location < location_limit) {
+ unsigned i = component;
+ while (i < last_comp) {
+ if (explicit_locations[location][i].var != NULL) {
+ linker_error(prog,
+ "%s shader has multiple outputs explicitly "
+ "assigned to location %d and component %d\n",
+ _mesa_shader_stage_to_string(stage),
+ location, component);
+ return false;
+ }
+
+ /* Make sure all component at this location have the same type.
+ */
+ for (unsigned j = 0; j < 4; j++) {
+ if (explicit_locations[location][j].var &&
+ explicit_locations[location][j].base_type !=
+ type->without_array()->base_type) {
+ linker_error(prog,
+ "Varyings sharing the same location must "
+ "have the same underlying numerical type. "
+ "Location %u component %u\n", location, component);
+ return false;
+ }
+ }
+
+ explicit_locations[location][i].var = var;
+ explicit_locations[location][i].base_type =
+ type->without_array()->base_type;
+ i++;
+
+ /* We need to do some special handling for doubles as dvec3 and
+ * dvec4 consume two consecutive locations. We don't need to
+ * worry about components beginning at anything other than 0 as
+ * the spec does not allow this for dvec3 and dvec4.
+ */
+ if (i == 4 && last_comp > 4) {
+ last_comp = last_comp - 4;
+ /* Bump location index and reset the component index */
+ location++;
+ i = 0;
+ }
+ }
+
+ location++;
+ }
+
+ return true;
+}
+
/**
* Validate that outputs from one stage match inputs of another
*/
void
-cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
+cross_validate_outputs_to_inputs(struct gl_context *ctx,
+ struct gl_shader_program *prog,
gl_linked_shader *producer,
gl_linked_shader *consumer)
{
glsl_symbol_table parameters;
- ir_variable *explicit_locations[MAX_VARYINGS_INCL_PATCH][4] =
- { {NULL, NULL} };
+ struct explicit_location_info explicit_locations[MAX_VARYING][4] = { 0 };
/* Find all shader outputs in the "producer" stage.
*/
*/
const glsl_type *type = get_varying_type(var, producer->Stage);
unsigned num_elements = type->count_attribute_slots(false);
- unsigned idx = var->data.location - VARYING_SLOT_VAR0;
+ unsigned idx = compute_variable_location_slot(var, producer->Stage);
unsigned slot_limit = idx + num_elements;
- unsigned last_comp;
- if (type->without_array()->is_record()) {
- /* The component qualifier can't be used on structs so just treat
- * all component slots as used.
- */
- last_comp = 4;
- } else {
- unsigned dmul = type->without_array()->is_64bit() ? 2 : 1;
- last_comp = var->data.location_frac +
- type->without_array()->vector_elements * dmul;
+ unsigned slot_max =
+ ctx->Const.Program[producer->Stage].MaxOutputComponents / 4;
+ if (slot_limit > slot_max) {
+ linker_error(prog,
+ "Invalid location %u in %s shader\n",
+ idx, _mesa_shader_stage_to_string(producer->Stage));
+ return;
}
- while (idx < slot_limit) {
- unsigned i = var->data.location_frac;
- while (i < last_comp) {
- if (explicit_locations[idx][i] != NULL) {
- linker_error(prog,
- "%s shader has multiple outputs explicitly "
- "assigned to location %d and component %d\n",
- _mesa_shader_stage_to_string(producer->Stage),
- idx, var->data.location_frac);
+ if (type->without_array()->is_interface()) {
+ for (unsigned i = 0; i < type->without_array()->length; i++) {
+ const glsl_type *field_type = type->fields.structure[i].type;
+ unsigned field_location = type->fields.structure[i].location -
+ (type->fields.structure[i].patch ? VARYING_SLOT_PATCH0 :
+ VARYING_SLOT_VAR0);
+ if (!check_location_aliasing(explicit_locations, var,
+ field_location,
+ 0, field_location + 1,
+ field_type, prog,
+ producer->Stage)) {
return;
}
-
- /* Make sure all component at this location have the same type.
- */
- for (unsigned j = 0; j < 4; j++) {
- if (explicit_locations[idx][j] &&
- (explicit_locations[idx][j]->type->without_array()
- ->base_type != type->without_array()->base_type)) {
- linker_error(prog,
- "Varyings sharing the same location must "
- "have the same underlying numerical type. "
- "Location %u component %u\n", idx,
- var->data.location_frac);
- return;
- }
- }
-
- explicit_locations[idx][i] = var;
- i++;
-
- /* We need to do some special handling for doubles as dvec3 and
- * dvec4 consume two consecutive locations. We don't need to
- * worry about components beginning at anything other than 0 as
- * the spec does not allow this for dvec3 and dvec4.
- */
- if (i == 4 && last_comp > 4) {
- last_comp = last_comp - 4;
- /* Bump location index and reset the component index */
- idx++;
- i = 0;
- }
}
- idx++;
+ } else if (!check_location_aliasing(explicit_locations, var,
+ idx, var->data.location_frac,
+ slot_limit, type, prog,
+ producer->Stage)) {
+ return;
}
}
}
const glsl_type *type = get_varying_type(input, consumer->Stage);
unsigned num_elements = type->count_attribute_slots(false);
- unsigned idx = input->data.location - VARYING_SLOT_VAR0;
+ unsigned idx =
+ compute_variable_location_slot(input, consumer->Stage);
unsigned slot_limit = idx + num_elements;
while (idx < slot_limit) {
- output = explicit_locations[idx][input->data.location_frac];
+ if (idx >= MAX_VARYING) {
+ linker_error(prog,
+ "Invalid location %u in %s shader\n", idx,
+ _mesa_shader_stage_to_string(consumer->Stage));
+ return;
+ }
+
+ output = explicit_locations[idx][input->data.location_frac].var;
if (output == NULL ||
input->data.location != output->data.location) {
* Demote shader inputs and outputs that are not used in other stages, and
* remove them via dead code elimination.
*/
-void
+static void
remove_unused_shader_inputs_and_outputs(bool is_separate_shader_object,
gl_linked_shader *sh,
enum ir_variable_mode mode)
unsigned actual_array_size;
switch (this->lowered_builtin_array_variable) {
case clip_distance:
- actual_array_size = prog->LastClipDistanceArraySize;
+ actual_array_size = prog->last_vert_prog ?
+ prog->last_vert_prog->info.clip_distance_array_size : 0;
break;
case cull_distance:
- actual_array_size = prog->LastCullDistanceArraySize;
+ actual_array_size = prog->last_vert_prog ?
+ prog->last_vert_prog->info.cull_distance_array_size : 0;
break;
case tess_level_outer:
actual_array_size = 4;
* If an error occurs, the error is reported through linker_error() and false
* is returned.
*/
-bool
+static bool
parse_tfeedback_decls(struct gl_context *ctx, struct gl_shader_program *prog,
const void *mem_ctx, unsigned num_names,
char **varying_names, tfeedback_decl *decls)
/**
* Store transform feedback location assignments into
- * prog->LinkedTransformFeedback based on the data stored in tfeedback_decls.
+ * prog->sh.LinkedTransformFeedback based on the data stored in
+ * tfeedback_decls.
*
* If an error occurs, the error is reported through linker_error() and false
* is returned.
*/
-bool
+static bool
store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog,
unsigned num_tfeedback_decls,
tfeedback_decl *tfeedback_decls, bool has_xfb_qualifiers)
{
+ if (!prog->last_vert_prog)
+ return true;
+
/* Make sure MaxTransformFeedbackBuffers is less than 32 so the bitmask for
* tracking the number of buffers doesn't overflow.
*/
bool separate_attribs_mode =
prog->TransformFeedback.BufferMode == GL_SEPARATE_ATTRIBS;
- ralloc_free(prog->LinkedTransformFeedback.Varyings);
- ralloc_free(prog->LinkedTransformFeedback.Outputs);
-
- memset(&prog->LinkedTransformFeedback, 0,
- sizeof(prog->LinkedTransformFeedback));
+ struct gl_program *xfb_prog = prog->last_vert_prog;
+ xfb_prog->sh.LinkedTransformFeedback =
+ rzalloc(xfb_prog, struct gl_transform_feedback_info);
/* The xfb_offset qualifier does not have to be used in increasing order
* however some drivers expect to receive the list of transform feedback
qsort(tfeedback_decls, num_tfeedback_decls, sizeof(*tfeedback_decls),
cmp_xfb_offset);
- prog->LinkedTransformFeedback.Varyings =
- rzalloc_array(prog,
- struct gl_transform_feedback_varying_info,
+ xfb_prog->sh.LinkedTransformFeedback->Varyings =
+ rzalloc_array(xfb_prog, struct gl_transform_feedback_varying_info,
num_tfeedback_decls);
unsigned num_outputs = 0;
num_outputs += tfeedback_decls[i].get_num_outputs();
}
- prog->LinkedTransformFeedback.Outputs =
- rzalloc_array(prog,
- struct gl_transform_feedback_output,
+ xfb_prog->sh.LinkedTransformFeedback->Outputs =
+ rzalloc_array(xfb_prog, struct gl_transform_feedback_output,
num_outputs);
unsigned num_buffers = 0;
if (!has_xfb_qualifiers && separate_attribs_mode) {
/* GL_SEPARATE_ATTRIBS */
for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
- if (!tfeedback_decls[i].store(ctx, prog, &prog->LinkedTransformFeedback,
+ if (!tfeedback_decls[i].store(ctx, prog,
+ xfb_prog->sh.LinkedTransformFeedback,
num_buffers, num_buffers, num_outputs,
NULL, has_xfb_qualifiers))
return false;
if (prog->TransformFeedback.BufferStride[j]) {
buffers |= 1 << j;
explicit_stride[j] = true;
- prog->LinkedTransformFeedback.Buffers[j].Stride =
+ xfb_prog->sh.LinkedTransformFeedback->Buffers[j].Stride =
prog->TransformFeedback.BufferStride[j] / 4;
}
}
if (tfeedback_decls[i].is_next_buffer_separator()) {
if (!tfeedback_decls[i].store(ctx, prog,
- &prog->LinkedTransformFeedback,
+ xfb_prog->sh.LinkedTransformFeedback,
buffer, num_buffers, num_outputs,
explicit_stride, has_xfb_qualifiers))
return false;
buffers |= 1 << buffer;
if (!tfeedback_decls[i].store(ctx, prog,
- &prog->LinkedTransformFeedback,
+ xfb_prog->sh.LinkedTransformFeedback,
buffer, num_buffers, num_outputs,
explicit_stride, has_xfb_qualifiers))
return false;
}
}
- assert(prog->LinkedTransformFeedback.NumOutputs == num_outputs);
+ assert(xfb_prog->sh.LinkedTransformFeedback->NumOutputs == num_outputs);
- prog->LinkedTransformFeedback.ActiveBuffers = buffers;
+ xfb_prog->sh.LinkedTransformFeedback->ActiveBuffers = buffers;
return true;
}
{
public:
varying_matches(bool disable_varying_packing, bool xfb_enabled,
+ bool enhanced_layouts_enabled,
gl_shader_stage producer_stage,
gl_shader_stage consumer_stage);
~varying_matches();
*/
const bool xfb_enabled;
+ const bool enhanced_layouts_enabled;
+
/**
* Enum representing the order in which varyings are packed within a
* packing class.
varying_matches::varying_matches(bool disable_varying_packing,
bool xfb_enabled,
+ bool enhanced_layouts_enabled,
gl_shader_stage producer_stage,
gl_shader_stage consumer_stage)
: disable_varying_packing(disable_varying_packing),
xfb_enabled(xfb_enabled),
+ enhanced_layouts_enabled(enhanced_layouts_enabled),
producer_stage(producer_stage),
consumer_stage(consumer_stage)
{
if (!disable_varying_packing &&
(needs_flat_qualifier ||
- (consumer_stage != -1 && consumer_stage != MESA_SHADER_FRAGMENT))) {
+ (consumer_stage != MESA_SHADER_NONE && consumer_stage != MESA_SHADER_FRAGMENT))) {
/* Since this varying is not being consumed by the fragment shader, its
* interpolation type varying cannot possibly affect rendering.
* Also, this variable is non-flat and is (or contains) an integer
? consumer_stage : producer_stage;
const glsl_type *type = get_varying_type(var, stage);
+ if (producer_var && consumer_var &&
+ consumer_var->data.must_be_shader_input) {
+ producer_var->data.must_be_shader_input = 1;
+ }
+
this->matches[this->num_matches].packing_class
= this->compute_packing_class(var);
this->matches[this->num_matches].packing_order
= this->compute_packing_order(var);
- if (this->disable_varying_packing && !is_varying_packing_safe(type, var)) {
+ if ((this->disable_varying_packing && !is_varying_packing_safe(type, var)) ||
+ var->data.must_be_shader_input) {
unsigned slots = type->count_attribute_slots(false);
this->matches[this->num_matches].num_components = slots * 4;
} else {
this->matches[this->num_matches].num_components
= type->component_slots();
}
+
this->matches[this->num_matches].producer_var = producer_var;
this->matches[this->num_matches].consumer_var = consumer_var;
this->num_matches++;
* we can pack varyings together that are only used for transform
* feedback.
*/
- if ((this->disable_varying_packing &&
+ if (var->data.must_be_shader_input ||
+ (this->disable_varying_packing &&
!(previous_var_xfb_only && var->data.is_xfb_only)) ||
(i > 0 && this->matches[i - 1].packing_class
!= this->matches[i].packing_class )) {
void
varying_matches::store_locations() const
{
+ /* Check is location needs to be packed with lower_packed_varyings() or if
+ * we can just use ARB_enhanced_layouts packing.
+ */
+ bool pack_loc[MAX_VARYINGS_INCL_PATCH] = { 0 };
+ const glsl_type *loc_type[MAX_VARYINGS_INCL_PATCH][4] = { {NULL, NULL} };
+
for (unsigned i = 0; i < this->num_matches; i++) {
ir_variable *producer_var = this->matches[i].producer_var;
ir_variable *consumer_var = this->matches[i].consumer_var;
consumer_var->data.location = VARYING_SLOT_VAR0 + slot;
consumer_var->data.location_frac = offset;
}
+
+ /* Find locations suitable for native packing via
+ * ARB_enhanced_layouts.
+ */
+ if (producer_var && consumer_var) {
+ if (enhanced_layouts_enabled) {
+ const glsl_type *type =
+ get_varying_type(producer_var, producer_stage);
+ if (type->is_array() || type->is_matrix() || type->is_record() ||
+ type->is_double()) {
+ unsigned comp_slots = type->component_slots() + offset;
+ unsigned slots = comp_slots / 4;
+ if (comp_slots % 4)
+ slots += 1;
+
+ for (unsigned j = 0; j < slots; j++) {
+ pack_loc[slot + j] = true;
+ }
+ } else if (offset + type->vector_elements > 4) {
+ pack_loc[slot] = true;
+ pack_loc[slot + 1] = true;
+ } else {
+ loc_type[slot][offset] = type;
+ }
+ }
+ }
+ }
+
+ /* Attempt to use ARB_enhanced_layouts for more efficient packing if
+ * suitable.
+ */
+ if (enhanced_layouts_enabled) {
+ for (unsigned i = 0; i < this->num_matches; i++) {
+ ir_variable *producer_var = this->matches[i].producer_var;
+ ir_variable *consumer_var = this->matches[i].consumer_var;
+ unsigned generic_location = this->matches[i].generic_location;
+ unsigned slot = generic_location / 4;
+
+ if (pack_loc[slot] || !producer_var || !consumer_var)
+ continue;
+
+ const glsl_type *type =
+ get_varying_type(producer_var, producer_stage);
+ bool type_match = true;
+ for (unsigned j = 0; j < 4; j++) {
+ if (loc_type[slot][j]) {
+ if (type->base_type != loc_type[slot][j]->base_type)
+ type_match = false;
+ }
+ }
+
+ if (type_match) {
+ producer_var->data.explicit_location = 1;
+ consumer_var->data.explicit_location = 1;
+ producer_var->data.explicit_component = 1;
+ consumer_var->data.explicit_component = 1;
+ }
+ }
}
}
* Therefore, the packing class depends only on the interpolation type.
*/
unsigned packing_class = var->data.centroid | (var->data.sample << 1) |
- (var->data.patch << 2);
- packing_class *= 4;
+ (var->data.patch << 2) |
+ (var->data.must_be_shader_input << 3);
+ packing_class *= 8;
packing_class += var->is_interpolation_flat()
? unsigned(INTERP_MODE_FLAT) : var->data.interpolation;
return packing_class;
{
const glsl_type *element_type = var->type;
- while (element_type->base_type == GLSL_TYPE_ARRAY) {
+ while (element_type->is_array()) {
element_type = element_type->fields.array;
}
this->toplevel_var = var;
this->varying_floats = 0;
- program_resource_visitor::process(var);
+ program_resource_visitor::process(var, false);
}
private:
virtual void visit_field(const glsl_type *type, const char *name,
- bool row_major)
+ bool /* row_major */,
+ const glsl_type * /* record_type */,
+ const enum glsl_interface_packing,
+ bool /* last_field */)
{
assert(!type->without_array()->is_record());
assert(!type->without_array()->is_interface());
- (void) row_major;
-
tfeedback_candidate *candidate
= rzalloc(this->mem_ctx, tfeedback_candidate);
candidate->toplevel_var = this->toplevel_var;
* 64 bit map. Per-vertex and per-patch both have separate location domains
* with a max of MAX_VARYING.
*/
-uint64_t
+static uint64_t
reserved_varying_slot(struct gl_linked_shader *stage,
ir_variable_mode io_mode)
{
var_slot = var->data.location - VARYING_SLOT_VAR0;
unsigned num_elements = get_varying_type(var, stage->Stage)
- ->count_attribute_slots(stage->Stage == MESA_SHADER_VERTEX);
+ ->count_attribute_slots(io_mode == ir_var_shader_in &&
+ stage->Stage == MESA_SHADER_VERTEX);
for (unsigned i = 0; i < num_elements; i++) {
if (var_slot >= 0 && var_slot < MAX_VARYINGS_INCL_PATCH)
slots |= UINT64_C(1) << var_slot;
* be NULL. In this case, varying locations are assigned solely based on the
* requirements of transform feedback.
*/
-bool
+static bool
assign_varying_locations(struct gl_context *ctx,
void *mem_ctx,
struct gl_shader_program *prog,
disable_varying_packing = true;
varying_matches matches(disable_varying_packing, xfb_enabled,
- producer ? producer->Stage : (gl_shader_stage)-1,
- consumer ? consumer->Stage : (gl_shader_stage)-1);
+ ctx->Extensions.ARB_enhanced_layouts,
+ producer ? producer->Stage : MESA_SHADER_NONE,
+ consumer ? consumer->Stage : MESA_SHADER_NONE);
hash_table *tfeedback_candidates =
_mesa_hash_table_create(NULL, _mesa_key_hash_string,
_mesa_key_string_equal);
}
}
- _mesa_hash_table_destroy(consumer_inputs, NULL);
- _mesa_hash_table_destroy(consumer_interface_inputs, NULL);
-
for (unsigned i = 0; i < num_tfeedback_decls; ++i) {
if (!tfeedback_decls[i].is_varying())
continue;
return false;
}
+ /* Mark xfb varyings as always active */
+ matched_candidate->toplevel_var->data.always_active_io = 1;
+
+ /* Mark any corresponding inputs as always active also. We must do this
+ * because we have a NIR pass that lowers vectors to scalars and another
+ * that removes unused varyings.
+ * We don't split varyings marked as always active because there is no
+ * point in doing so. This means we need to mark both sides of the
+ * interface as always active otherwise we will have a mismatch and
+ * start removing things we shouldn't.
+ */
+ ir_variable *const input_var =
+ linker::get_matching_input(mem_ctx, matched_candidate->toplevel_var,
+ consumer_inputs,
+ consumer_interface_inputs,
+ consumer_inputs_with_locations);
+ if (input_var)
+ input_var->data.always_active_io = 1;
+
if (matched_candidate->toplevel_var->data.is_unmatched_generic_inout) {
matched_candidate->toplevel_var->data.is_xfb_only = 1;
matches.record(matched_candidate->toplevel_var, NULL);
}
}
+ _mesa_hash_table_destroy(consumer_inputs, NULL);
+ _mesa_hash_table_destroy(consumer_interface_inputs, NULL);
+
uint8_t components[MAX_VARYINGS_INCL_PATCH] = {0};
const unsigned slots_used = matches.assign_locations(
prog, components, reserved_slots);
if (var && var->data.mode == ir_var_shader_in &&
var->data.is_unmatched_generic_inout) {
- if (!prog->IsES && prog->Version <= 120) {
+ if (!prog->IsES && prog->data->Version <= 120) {
/* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:
*
* Only those varying variables used (i.e. read) in
return true;
}
-bool
+static bool
check_against_output_limit(struct gl_context *ctx,
struct gl_shader_program *prog,
gl_linked_shader *producer,
return true;
}
-bool
+static bool
check_against_input_limit(struct gl_context *ctx,
struct gl_shader_program *prog,
gl_linked_shader *consumer,
return true;
}
+
+bool
+link_varyings(struct gl_shader_program *prog, unsigned first, unsigned last,
+ struct gl_context *ctx, void *mem_ctx)
+{
+ bool has_xfb_qualifiers = false;
+ unsigned num_tfeedback_decls = 0;
+ char **varying_names = NULL;
+ tfeedback_decl *tfeedback_decls = NULL;
+
+ /* 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],
+ prog, &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 = rzalloc_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];
+
+ do_dead_builtin_varyings(ctx, NULL, sh, 0, NULL);
+ do_dead_builtin_varyings(ctx, sh, NULL, num_tfeedback_decls,
+ tfeedback_decls);
+
+ 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;
+ }
+ } 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);
+
+ do_dead_builtin_varyings(ctx, sh_i, sh_next,
+ next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
+ tfeedback_decls);
+
+ 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;
+
+ /* 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;
+
+ return true;
+}