static unsigned
values_for_type(const glsl_type *type)
{
- if (type->is_sampler()) {
+ if (type->is_sampler() || type->is_subroutine()) {
return 1;
- } else if (type->is_array() && type->fields.array->is_sampler()) {
+ } else if (type->is_array() && (type->fields.array->is_sampler() ||
+ type->fields.array->is_subroutine())) {
return type->array_size();
} else {
return type->component_slots();
count_uniform_size(struct string_to_uint_map *map)
: num_active_uniforms(0), num_values(0), num_shader_samplers(0),
num_shader_images(0), num_shader_uniform_components(0),
+ num_shader_subroutines(0),
is_ubo_var(false), map(map)
{
/* empty */
this->num_shader_samplers = 0;
this->num_shader_images = 0;
this->num_shader_uniform_components = 0;
+ this->num_shader_subroutines = 0;
}
void process(ir_variable *var)
*/
unsigned num_shader_uniform_components;
+ /**
+ * Number of subroutine uniforms used
+ */
+ unsigned num_shader_subroutines;
+
bool is_ubo_var;
private:
* count it for each shader target.
*/
const unsigned values = values_for_type(type);
- if (type->contains_sampler()) {
+ if (type->contains_subroutine()) {
+ this->num_shader_subroutines += values;
+ } else if (type->contains_sampler()) {
this->num_shader_samplers += values;
} else if (type->contains_image()) {
this->num_shader_images += values;
this->shader_shadow_samplers = 0;
this->next_sampler = 0;
this->next_image = 0;
+ this->next_subroutine = 0;
memset(this->targets, 0, sizeof(this->targets));
}
}
}
+ void handle_subroutines(const glsl_type *base_type,
+ struct gl_uniform_storage *uniform)
+ {
+ if (base_type->is_subroutine()) {
+ uniform->subroutine[shader_type].index = this->next_subroutine;
+ uniform->subroutine[shader_type].active = true;
+
+ /* Increment the subroutine index by 1 for non-arrays and by the
+ * number of array elements for arrays.
+ */
+ this->next_subroutine += MAX2(1, uniform->array_elements);
+
+ } else {
+ uniform->subroutine[shader_type].index = ~0;
+ uniform->subroutine[shader_type].active = false;
+ }
+ }
+
virtual void visit_field(const glsl_type *type, const char *name,
bool row_major)
{
/* This assigns uniform indices to sampler and image uniforms. */
handle_samplers(base_type, &this->uniforms[id]);
handle_images(base_type, &this->uniforms[id]);
+ handle_subroutines(base_type, &this->uniforms[id]);
/* If there is already storage associated with this uniform or if the
* uniform is set as builtin, it means that it was set while processing
struct gl_uniform_storage *uniforms;
unsigned next_sampler;
unsigned next_image;
+ unsigned next_subroutine;
public:
union gl_constant_value *values;
sh->num_samplers = uniform_size.num_shader_samplers;
sh->NumImages = uniform_size.num_shader_images;
sh->num_uniform_components = uniform_size.num_shader_uniform_components;
-
sh->num_combined_uniform_components = sh->num_uniform_components;
+
for (unsigned i = 0; i < sh->NumUniformBlocks; i++) {
sh->num_combined_uniform_components +=
sh->UniformBlocks[i].UniformBufferSize / 4;
/* Reserve all the explicit locations of the active uniforms. */
for (unsigned i = 0; i < num_uniforms; i++) {
+ if (uniforms[i].type->is_subroutine())
+ continue;
+
if (uniforms[i].remap_location != UNMAPPED_UNIFORM_LOC) {
/* How many new entries for this uniform? */
const unsigned entries = MAX2(1, uniforms[i].array_elements);
/* Reserve locations for rest of the uniforms. */
for (unsigned i = 0; i < num_uniforms; i++) {
+ if (uniforms[i].type->is_subroutine())
+ continue;
/* Built-in uniforms should not get any location. */
if (uniforms[i].builtin)
continue;
prog->NumUniformRemapTable += entries;
}
+ /* Reserve all the explicit locations of the active subroutine uniforms. */
+ for (unsigned i = 0; i < num_uniforms; i++) {
+ if (!uniforms[i].type->is_subroutine())
+ continue;
+
+ if (uniforms[i].remap_location == UNMAPPED_UNIFORM_LOC)
+ continue;
+
+ for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) {
+ struct gl_shader *sh = prog->_LinkedShaders[j];
+ if (!sh)
+ continue;
+
+ if (!uniforms[i].subroutine[j].active)
+ continue;
+
+ /* How many new entries for this uniform? */
+ const unsigned entries = MAX2(1, uniforms[i].array_elements);
+
+ /* Set remap table entries point to correct gl_uniform_storage. */
+ for (unsigned k = 0; k < entries; k++) {
+ unsigned element_loc = uniforms[i].remap_location + k;
+ assert(sh->SubroutineUniformRemapTable[element_loc] ==
+ INACTIVE_UNIFORM_EXPLICIT_LOCATION);
+ sh->SubroutineUniformRemapTable[element_loc] = &uniforms[i];
+ }
+ }
+ }
+
+ /* reserve subroutine locations */
+ for (unsigned i = 0; i < num_uniforms; i++) {
+
+ if (!uniforms[i].type->is_subroutine())
+ continue;
+ const unsigned entries = MAX2(1, uniforms[i].array_elements);
+
+ if (uniforms[i].remap_location != UNMAPPED_UNIFORM_LOC)
+ continue;
+ for (unsigned j = 0; j < MESA_SHADER_STAGES; j++) {
+ struct gl_shader *sh = prog->_LinkedShaders[j];
+ if (!sh)
+ continue;
+
+ if (!uniforms[i].subroutine[j].active)
+ continue;
+
+ sh->SubroutineUniformRemapTable =
+ reralloc(sh,
+ sh->SubroutineUniformRemapTable,
+ gl_uniform_storage *,
+ sh->NumSubroutineUniformRemapTable + entries);
+
+ for (unsigned k = 0; k < entries; k++)
+ sh->SubroutineUniformRemapTable[sh->NumSubroutineUniformRemapTable + k] = &uniforms[i];
+ uniforms[i].remap_location = sh->NumSubroutineUniformRemapTable;
+ sh->NumSubroutineUniformRemapTable += entries;
+ }
+ }
+
#ifndef NDEBUG
for (unsigned i = 0; i < num_uniforms; i++) {
assert(uniforms[i].storage != NULL || uniforms[i].builtin);
if (uniforms_only && (var->data.mode != ir_var_uniform && var->data.mode != ir_var_shader_storage))
continue;
+ /* don't cross validate subroutine uniforms */
+ if (var->type->contains_subroutine())
+ continue;
+
/* Don't cross validate temporaries that are at global scope. These
* will eventually get pulled into the shaders 'main'.
*/
* Atomic counters are supposed to get deterministic
* locations assigned based on the declaration ordering and
* sizes, array compaction would mess that up.
+ *
+ * Subroutine uniforms are not removed.
*/
- if (var->is_in_buffer_block() || var->type->contains_atomic())
+ if (var->is_in_buffer_block() || var->type->contains_atomic() ||
+ var->type->contains_subroutine())
continue;
unsigned int size = var->data.max_array_access;
}
}
+static void
+link_calculate_subroutine_compat(struct gl_context *ctx, struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_shader *sh = prog->_LinkedShaders[i];
+ int count;
+ if (!sh)
+ continue;
+
+ for (unsigned j = 0; j < sh->NumSubroutineUniformRemapTable; j++) {
+ struct gl_uniform_storage *uni = sh->SubroutineUniformRemapTable[j];
+
+ if (!uni)
+ continue;
+
+ count = 0;
+ for (unsigned f = 0; f < sh->NumSubroutineFunctions; f++) {
+ struct gl_subroutine_function *fn = &sh->SubroutineFunctions[f];
+ for (int k = 0; k < fn->num_compat_types; k++) {
+ if (fn->types[k] == uni->type) {
+ count++;
+ break;
+ }
+ }
+ }
+ uni->num_compatible_subroutines = count;
+ }
+ }
+}
+
+static void
+check_subroutine_resources(struct gl_context *ctx, struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_shader *sh = prog->_LinkedShaders[i];
+
+ if (sh) {
+ if (sh->NumSubroutineUniformRemapTable > MAX_SUBROUTINE_UNIFORM_LOCATIONS)
+ linker_error(prog, "Too many %s shader subroutine uniforms\n",
+ _mesa_shader_stage_to_string(i));
+ }
+ }
+}
/**
* Validate shader image resources.
*/
return true;
}
+static bool
+reserve_subroutine_explicit_locations(struct gl_shader_program *prog,
+ struct gl_shader *sh,
+ ir_variable *var)
+{
+ unsigned slots = var->type->uniform_locations();
+ unsigned max_loc = var->data.location + slots - 1;
+
+ /* Resize remap table if locations do not fit in the current one. */
+ if (max_loc + 1 > sh->NumSubroutineUniformRemapTable) {
+ sh->SubroutineUniformRemapTable =
+ reralloc(sh, sh->SubroutineUniformRemapTable,
+ gl_uniform_storage *,
+ max_loc + 1);
+
+ if (!sh->SubroutineUniformRemapTable) {
+ linker_error(prog, "Out of memory during linking.\n");
+ return false;
+ }
+
+ /* Initialize allocated space. */
+ for (unsigned i = sh->NumSubroutineUniformRemapTable; i < max_loc + 1; i++)
+ sh->SubroutineUniformRemapTable[i] = NULL;
+
+ sh->NumSubroutineUniformRemapTable = max_loc + 1;
+ }
+
+ for (unsigned i = 0; i < slots; i++) {
+ unsigned loc = var->data.location + i;
+
+ /* Check if location is already used. */
+ if (sh->SubroutineUniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
+
+ /* ARB_explicit_uniform_location specification states:
+ * "No two subroutine uniform variables can have the same location
+ * in the same shader stage, otherwise a compiler or linker error
+ * will be generated."
+ */
+ linker_error(prog,
+ "location qualifier for uniform %s overlaps "
+ "previously used location\n",
+ var->name);
+ return false;
+ }
+
+ /* Initialize location as inactive before optimization
+ * rounds and location assignment.
+ */
+ sh->SubroutineUniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
+ }
+
+ return true;
+}
/**
* Check and reserve all explicit uniform locations, called before
* any optimizations happen to handle also inactive uniforms and
ir_variable *var = node->as_variable();
if (var && (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage) &&
var->data.explicit_location) {
- if (!reserve_explicit_locations(prog, uniform_map, var)) {
+ bool ret;
+ if (var->type->is_subroutine())
+ ret = reserve_subroutine_explicit_locations(prog, sh, var);
+ else
+ ret = reserve_explicit_locations(prog, uniform_map, var);
+ if (!ret) {
delete uniform_map;
return;
}
return;
}
+ for (unsigned i = 0; i < shProg->NumUniformStorage; i++) {
+ GLenum type;
+ if (!shProg->UniformStorage[i].hidden)
+ continue;
+
+ for (int j = MESA_SHADER_VERTEX; j < MESA_SHADER_STAGES; j++) {
+ if (!shProg->UniformStorage[i].subroutine[j].active)
+ continue;
+
+ type = _mesa_shader_stage_to_subroutine_uniform((gl_shader_stage)j);
+ /* add shader subroutines */
+ if (!add_program_resource(shProg, type, &shProg->UniformStorage[i], 0))
+ return;
+ }
+ }
+
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ struct gl_shader *sh = shProg->_LinkedShaders[i];
+ GLuint type;
+
+ if (!sh)
+ continue;
+
+ type = _mesa_shader_stage_to_subroutine((gl_shader_stage)i);
+ for (unsigned j = 0; j < sh->NumSubroutineFunctions; j++) {
+ if (!add_program_resource(shProg, type, &sh->SubroutineFunctions[j], 0))
+ return;
+ }
+ }
+
/* TODO - following extensions will require more resource types:
*
* GL_ARB_shader_storage_buffer_object
- * GL_ARB_shader_subroutine
*/
}
return true;
}
+void
+link_assign_subroutine_types(struct gl_context *ctx,
+ struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+ gl_shader *sh = prog->_LinkedShaders[i];
+
+ if (sh == NULL)
+ continue;
+
+ foreach_in_list(ir_instruction, node, sh->ir) {
+ ir_function *fn = node->as_function();
+ if (!fn)
+ continue;
+
+ if (fn->is_subroutine)
+ sh->NumSubroutineUniformTypes++;
+
+ if (!fn->num_subroutine_types)
+ continue;
+
+ sh->SubroutineFunctions = reralloc(sh, sh->SubroutineFunctions,
+ struct gl_subroutine_function,
+ sh->NumSubroutineFunctions + 1);
+ sh->SubroutineFunctions[sh->NumSubroutineFunctions].name = ralloc_strdup(sh, fn->name);
+ sh->SubroutineFunctions[sh->NumSubroutineFunctions].num_compat_types = fn->num_subroutine_types;
+ sh->SubroutineFunctions[sh->NumSubroutineFunctions].types =
+ ralloc_array(sh, const struct glsl_type *,
+ fn->num_subroutine_types);
+ for (int j = 0; j < fn->num_subroutine_types; j++)
+ sh->SubroutineFunctions[sh->NumSubroutineFunctions].types[j] = fn->subroutine_types[j];
+ sh->NumSubroutineFunctions++;
+ }
+ }
+}
void
link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
}
check_explicit_uniform_locations(ctx, prog);
+ link_assign_subroutine_types(ctx, prog);
+
if (!prog->LinkStatus)
goto done;
link_assign_atomic_counter_resources(ctx, prog);
store_fragdepth_layout(prog);
+ link_calculate_subroutine_compat(ctx, prog);
check_resources(ctx, prog);
+ check_subroutine_resources(ctx, prog);
check_image_resources(ctx, prog);
link_check_atomic_counter_resources(ctx, prog);