[Mesa-dev] [PATCH 2/2] mesa: fix GLSL program objects with more than 16 samplers combined
Marek Olšák
maraeo at gmail.com
Sat May 18 11:20:41 PDT 2013
Ping. This patch allows more than 16 samplers in all shaders combined
(MAX_COMBINED_TEXTURE_IMAGE_UNITS), which has never been possible with
current Mesa git. GL 3.1 requires 32 combined texture image units and
GL 3.2 requires 48, so I think this patch deserves a review.
Marek
On Mon, May 13, 2013 at 6:42 PM, Marek Olšák <maraeo at gmail.com> wrote:
> The problem is the sampler units are allocated from the same pool for all
> shader stages, so if a vertex shader uses 12 samplers (0..11), the fragment
> shader samplers start at index 12, leaving only 4 sampler units
> for the fragment shader. The main cause is probably the fact that samplers
> (texture unit -> sampler unit mapping, etc.) are tracked globally
> for an entire program object.
>
> This commit adapts the GLSL linker and core Mesa such that the sampler units
> are assigned to sampler uniforms for each shader stage separately
> (if a sampler uniform is used in all shader stages, it may occupy a different
> sampler unit in each, and vice versa, an i-th sampler unit may refer to
> a different sampler uniform in each shader stage), and the sampler-specific
> variables are moved from gl_shader_program to gl_shader.
>
> This doesn't require any driver changes, and it fixes piglit/max-samplers
> for gallium and classic swrast. It also works with any number of shader
> stages.
> ---
> src/glsl/ir_uniform.h | 27 +++--
> src/glsl/link_uniform_initializers.cpp | 25 ++++-
> src/glsl/link_uniforms.cpp | 127 +++++++++++-----------
> src/glsl/tests/set_uniform_initializer_tests.cpp | 10 +-
> src/mesa/main/mtypes.h | 22 ++--
> src/mesa/main/uniform_query.cpp | 24 ++--
> src/mesa/main/uniforms.c | 11 +-
> src/mesa/program/ir_to_mesa.cpp | 15 ++-
> src/mesa/program/sampler.cpp | 19 +++-
> 9 files changed, 167 insertions(+), 113 deletions(-)
>
> diff --git a/src/glsl/ir_uniform.h b/src/glsl/ir_uniform.h
> index 30e6f26..8198c48 100644
> --- a/src/glsl/ir_uniform.h
> +++ b/src/glsl/ir_uniform.h
> @@ -99,15 +99,24 @@ struct gl_uniform_storage {
> */
> bool initialized;
>
> - /**
> - * Base sampler index
> - *
> - * If \c ::base_type is \c GLSL_TYPE_SAMPLER, this represents the index of
> - * this sampler. If \c ::array_elements is not zero, the array will use
> - * sampler indexes \c ::sampler through \c ::sampler + \c ::array_elements
> - * - 1, inclusive.
> - */
> - uint8_t sampler;
> + struct {
> + /**
> + * Base sampler index
> + *
> + * If \c ::base_type is \c GLSL_TYPE_SAMPLER, this represents the index
> + * of this sampler. If \c ::array_elements is not zero, the array will
> + * use sampler indices \c ::sampler through \c ::sampler +
> + * \c ::array_elements - 1, inclusive.
> + *
> + * Note that the index may be different in each shader stage.
> + */
> + uint8_t index;
> +
> + /**
> + * Whether this sampler is used in this shader stage.
> + */
> + bool active;
> + } sampler[MESA_SHADER_TYPES];
>
> /**
> * Storage used by the driver for the uniform
> diff --git a/src/glsl/link_uniform_initializers.cpp b/src/glsl/link_uniform_initializers.cpp
> index 836a360..54d9bf1 100644
> --- a/src/glsl/link_uniform_initializers.cpp
> +++ b/src/glsl/link_uniform_initializers.cpp
> @@ -138,8 +138,16 @@ set_uniform_initializer(void *mem_ctx, gl_shader_program *prog,
> }
>
> if (base_type == GLSL_TYPE_SAMPLER) {
> - for (unsigned int i = 0; i < storage->array_elements; i++) {
> - prog->SamplerUnits[storage->sampler + i] = storage->storage[i].i;
> + for (int sh = 0; sh < MESA_SHADER_TYPES; sh++) {
> + gl_shader *shader = prog->_LinkedShaders[sh];
> +
> + if (shader && storage->sampler[sh].active) {
> + for (unsigned i = 0; i < storage->array_elements; i++) {
> + unsigned index = storage->sampler[sh].index + i;
> +
> + shader->SamplerUnits[index] = storage->storage[i].i;
> + }
> + }
> }
> }
> } else {
> @@ -148,8 +156,17 @@ set_uniform_initializer(void *mem_ctx, gl_shader_program *prog,
> val->type->base_type,
> val->type->components());
>
> - if (storage->type->is_sampler())
> - prog->SamplerUnits[storage->sampler] = storage->storage[0].i;
> + if (storage->type->is_sampler()) {
> + for (int sh = 0; sh < MESA_SHADER_TYPES; sh++) {
> + gl_shader *shader = prog->_LinkedShaders[sh];
> +
> + if (shader && storage->sampler[sh].active) {
> + unsigned index = storage->sampler[sh].index;
> +
> + shader->SamplerUnits[index] = storage->storage[0].i;
> + }
> + }
> + }
> }
>
> storage->initialized = true;
> diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp
> index d457e4d..3d02fff 100644
> --- a/src/glsl/link_uniforms.cpp
> +++ b/src/glsl/link_uniforms.cpp
> @@ -263,15 +263,19 @@ public:
> parcel_out_uniform_storage(struct string_to_uint_map *map,
> struct gl_uniform_storage *uniforms,
> union gl_constant_value *values)
> - : map(map), uniforms(uniforms), next_sampler(0), values(values)
> + : map(map), uniforms(uniforms), values(values)
> {
> - memset(this->targets, 0, sizeof(this->targets));
> }
>
> - void start_shader()
> + void start_shader(gl_shader_type shader_type)
> {
> + assert(shader_type < MESA_SHADER_TYPES);
> + this->shader_type = shader_type;
> +
> this->shader_samplers_used = 0;
> this->shader_shadow_samplers = 0;
> + this->next_sampler = 0;
> + memset(this->targets, 0, sizeof(this->targets));
> }
>
> void set_and_process(struct gl_shader_program *prog,
> @@ -335,8 +339,38 @@ public:
> int ubo_block_index;
> int ubo_byte_offset;
> bool ubo_row_major;
> + gl_shader_type shader_type;
>
> private:
> + void handle_samplers(const glsl_type *base_type,
> + struct gl_uniform_storage *uniform)
> + {
> +
> + if (base_type->is_sampler()) {
> + uniform->sampler[shader_type].index = this->next_sampler;
> + uniform->sampler[shader_type].active = true;
> +
> + /* Increment the sampler by 1 for non-arrays and by the number of
> + * array elements for arrays.
> + */
> + this->next_sampler +=
> + MAX2(1, uniform->array_elements);
> +
> + const gl_texture_index target = base_type->sampler_index();
> + const unsigned shadow = base_type->sampler_shadow;
> + for (unsigned i = uniform->sampler[shader_type].index;
> + i < MIN2(this->next_sampler, MAX_SAMPLERS);
> + i++) {
> + this->targets[i] = target;
> + this->shader_samplers_used |= 1U << i;
> + this->shader_shadow_samplers |= shadow << i;
> + }
> + } else {
> + uniform->sampler[shader_type].index = ~0;
> + uniform->sampler[shader_type].active = false;
> + }
> + }
> +
> virtual void visit_field(const glsl_type *type, const char *name,
> bool row_major)
> {
> @@ -354,31 +388,6 @@ private:
> if (!found)
> return;
>
> - /* If there is already storage associated with this uniform, it means
> - * that it was set while processing an earlier shader stage. For
> - * example, we may be processing the uniform in the fragment shader, but
> - * the uniform was already processed in the vertex shader.
> - */
> - if (this->uniforms[id].storage != NULL) {
> - /* If the uniform already has storage set from another shader stage,
> - * mark the samplers used for this shader stage.
> - */
> - if (type->contains_sampler()) {
> - const unsigned count = MAX2(1, this->uniforms[id].array_elements);
> - const unsigned shadow = (type->is_array())
> - ? type->fields.array->sampler_shadow : type->sampler_shadow;
> -
> - for (unsigned i = 0; i < count; i++) {
> - const unsigned s = this->uniforms[id].sampler + i;
> -
> - this->shader_samplers_used |= 1U << s;
> - this->shader_shadow_samplers |= shadow << s;
> - }
> - }
> -
> - return;
> - }
> -
> const glsl_type *base_type;
> if (type->is_array()) {
> this->uniforms[id].array_elements = type->length;
> @@ -388,26 +397,16 @@ private:
> base_type = type;
> }
>
> - if (base_type->is_sampler()) {
> - this->uniforms[id].sampler = this->next_sampler;
> + /* This assigns sampler uniforms to sampler units. */
> + handle_samplers(base_type, &this->uniforms[id]);
>
> - /* Increment the sampler by 1 for non-arrays and by the number of
> - * array elements for arrays.
> - */
> - this->next_sampler += MAX2(1, this->uniforms[id].array_elements);
> -
> - const gl_texture_index target = base_type->sampler_index();
> - const unsigned shadow = base_type->sampler_shadow;
> - for (unsigned i = this->uniforms[id].sampler
> - ; i < MIN2(this->next_sampler, MAX_SAMPLERS)
> - ; i++) {
> - this->targets[i] = target;
> - this->shader_samplers_used |= 1U << i;
> - this->shader_shadow_samplers |= shadow << i;
> - }
> -
> - } else {
> - this->uniforms[id].sampler = ~0;
> + /* If there is already storage associated with this uniform, it means
> + * that it was set while processing an earlier shader stage. For
> + * example, we may be processing the uniform in the fragment shader, but
> + * the uniform was already processed in the vertex shader.
> + */
> + if (this->uniforms[id].storage != NULL) {
> + return;
> }
>
> this->uniforms[id].name = ralloc_strdup(this->uniforms, name);
> @@ -633,17 +632,6 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
> prog->UniformHash = new string_to_uint_map;
> }
>
> - /* Uniforms that lack an initializer in the shader code have an initial
> - * value of zero. This includes sampler uniforms.
> - *
> - * Page 24 (page 30 of the PDF) of the GLSL 1.20 spec says:
> - *
> - * "The link time initial value is either the value of the variable's
> - * initializer, if present, or 0 if no initializer is present. Sampler
> - * types cannot have initializers."
> - */
> - memset(prog->SamplerUnits, 0, sizeof(prog->SamplerUnits));
> -
> /* First pass: Count the uniform resources used by the user-defined
> * uniforms. While this happens, each active uniform will have an index
> * assigned to it.
> @@ -656,6 +644,18 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
> if (prog->_LinkedShaders[i] == NULL)
> continue;
>
> + /* Uniforms that lack an initializer in the shader code have an initial
> + * value of zero. This includes sampler uniforms.
> + *
> + * Page 24 (page 30 of the PDF) of the GLSL 1.20 spec says:
> + *
> + * "The link time initial value is either the value of the variable's
> + * initializer, if present, or 0 if no initializer is present. Sampler
> + * types cannot have initializers."
> + */
> + memset(prog->_LinkedShaders[i]->SamplerUnits, 0,
> + sizeof(gl_shader::SamplerUnits));
> +
> link_update_uniform_buffer_variables(prog->_LinkedShaders[i]);
>
> /* Reset various per-shader target counts.
> @@ -706,9 +706,7 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
> if (prog->_LinkedShaders[i] == NULL)
> continue;
>
> - /* Reset various per-shader target counts.
> - */
> - parcel.start_shader();
> + parcel.start_shader((gl_shader_type)i);
>
> foreach_list(node, prog->_LinkedShaders[i]->ir) {
> ir_variable *const var = ((ir_instruction *) node)->as_variable();
> @@ -726,10 +724,11 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
>
> prog->_LinkedShaders[i]->active_samplers = parcel.shader_samplers_used;
> prog->_LinkedShaders[i]->shadow_samplers = parcel.shader_shadow_samplers;
> - }
>
> - assert(sizeof(prog->SamplerTargets) == sizeof(parcel.targets));
> - memcpy(prog->SamplerTargets, parcel.targets, sizeof(prog->SamplerTargets));
> + assert(sizeof(gl_shader::SamplerTargets) == sizeof(parcel.targets));
> + memcpy(prog->_LinkedShaders[i]->SamplerTargets, parcel.targets,
> + sizeof(gl_shader::SamplerTargets));
> + }
>
> #ifndef NDEBUG
> for (unsigned i = 0; i < num_user_uniforms; i++) {
> diff --git a/src/glsl/tests/set_uniform_initializer_tests.cpp b/src/glsl/tests/set_uniform_initializer_tests.cpp
> index 55831f9..5c6d4a5 100644
> --- a/src/glsl/tests/set_uniform_initializer_tests.cpp
> +++ b/src/glsl/tests/set_uniform_initializer_tests.cpp
> @@ -116,7 +116,10 @@ establish_uniform_storage(struct gl_shader_program *prog, unsigned num_storage,
> prog->UniformStorage[index_to_set].type = type;
> prog->UniformStorage[index_to_set].array_elements = array_size;
> prog->UniformStorage[index_to_set].initialized = false;
> - prog->UniformStorage[index_to_set].sampler = ~0;
> + for (int sh = 0; sh < MESA_SHADER_TYPES; sh++) {
> + prog->UniformStorage[index_to_set].sampler[sh].index = ~0;
> + prog->UniformStorage[index_to_set].sampler[sh].active = false;
> + }
> prog->UniformStorage[index_to_set].num_driver_storage = 0;
> prog->UniformStorage[index_to_set].driver_storage = NULL;
> prog->UniformStorage[index_to_set].storage =
> @@ -134,7 +137,10 @@ establish_uniform_storage(struct gl_shader_program *prog, unsigned num_storage,
> prog->UniformStorage[i].type = glsl_type::void_type;
> prog->UniformStorage[i].array_elements = 0;
> prog->UniformStorage[i].initialized = false;
> - prog->UniformStorage[i].sampler = ~0;
> + for (int sh = 0; sh < MESA_SHADER_TYPES; sh++) {
> + prog->UniformStorage[i].sampler[sh].index = ~0;
> + prog->UniformStorage[i].sampler[sh].active = false;
> + }
> prog->UniformStorage[i].num_driver_storage = 0;
> prog->UniformStorage[i].driver_storage = NULL;
> prog->UniformStorage[i].storage = NULL;
> diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
> index a68862d..bce1e6c 100644
> --- a/src/mesa/main/mtypes.h
> +++ b/src/mesa/main/mtypes.h
> @@ -2132,6 +2132,17 @@ struct gl_shader
> /*@}*/
>
> /**
> + * Map from sampler unit to texture unit (set by glUniform1i())
> + *
> + * A sampler unit is associated with each sampler uniform by the linker.
> + * The sampler unit associated with each uniform is stored in the
> + * \c gl_uniform_storage::sampler field.
> + */
> + GLubyte SamplerUnits[MAX_SAMPLERS];
> + /** Which texture target is being sampled (TEXTURE_1D/2D/3D/etc_INDEX) */
> + gl_texture_index SamplerTargets[MAX_SAMPLERS];
> +
> + /**
> * Number of uniform components used by this shader.
> *
> * This field is only set post-linking.
> @@ -2335,17 +2346,6 @@ struct gl_shader_program
> */
> struct string_to_uint_map *UniformHash;
>
> - /**
> - * Map from sampler unit to texture unit (set by glUniform1i())
> - *
> - * A sampler unit is associated with each sampler uniform by the linker.
> - * The sampler unit associated with each uniform is stored in the
> - * \c gl_uniform_storage::sampler field.
> - */
> - GLubyte SamplerUnits[MAX_SAMPLERS];
> - /** Which texture target is being sampled (TEXTURE_1D/2D/3D/etc_INDEX) */
> - gl_texture_index SamplerTargets[MAX_SAMPLERS];
> -
> GLboolean LinkStatus; /**< GL_LINK_STATUS */
> GLboolean Validated;
> GLboolean _Used; /**< Ever used for drawing? */
> diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp
> index b7f25e0..ec31049 100644
> --- a/src/mesa/main/uniform_query.cpp
> +++ b/src/mesa/main/uniform_query.cpp
> @@ -778,24 +778,24 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
> if (uni->type->is_sampler()) {
> int i;
>
> - for (i = 0; i < count; i++) {
> - shProg->SamplerUnits[uni->sampler + offset + i] =
> - ((unsigned *) values)[i];
> - }
> -
> bool flushed = false;
> for (i = 0; i < MESA_SHADER_TYPES; i++) {
> struct gl_shader *const sh = shProg->_LinkedShaders[i];
> + int j;
>
> - /* If the shader stage doesn't use any samplers, don't bother
> - * checking if any samplers have changed.
> + /* If the shader stage doesn't use the sampler uniform, skip this.
> */
> - if (sh == NULL || sh->active_samplers == 0)
> + if (sh == NULL || !uni->sampler[i].active)
> continue;
>
> + for (j = 0; j < count; j++) {
> + sh->SamplerUnits[uni->sampler[i].index + offset + j] =
> + ((unsigned *) values)[j];
> + }
> +
> struct gl_program *const prog = sh->Program;
>
> - assert(sizeof(prog->SamplerUnits) == sizeof(shProg->SamplerUnits));
> + assert(sizeof(prog->SamplerUnits) == sizeof(sh->SamplerUnits));
>
> /* Determine if any of the samplers used by this shader stage have
> * been modified.
> @@ -803,7 +803,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
> bool changed = false;
> for (unsigned j = 0; j < Elements(prog->SamplerUnits); j++) {
> if ((sh->active_samplers & (1U << j)) != 0
> - && (prog->SamplerUnits[j] != shProg->SamplerUnits[j])) {
> + && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) {
> changed = true;
> break;
> }
> @@ -816,8 +816,8 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg,
> }
>
> memcpy(prog->SamplerUnits,
> - shProg->SamplerUnits,
> - sizeof(shProg->SamplerUnits));
> + sh->SamplerUnits,
> + sizeof(sh->SamplerUnits));
>
> _mesa_update_shader_textures_used(shProg, prog);
> if (ctx->Driver.SamplerUniformChange)
> diff --git a/src/mesa/main/uniforms.c b/src/mesa/main/uniforms.c
> index f0d80f0..6d79df6 100644
> --- a/src/mesa/main/uniforms.c
> +++ b/src/mesa/main/uniforms.c
> @@ -45,6 +45,7 @@
> #include "main/enums.h"
> #include "ir_uniform.h"
> #include "glsl_types.h"
> +#include "program/program.h"
>
> /**
> * Update the vertex/fragment program's TexturesUsed array.
> @@ -66,14 +67,18 @@ _mesa_update_shader_textures_used(struct gl_shader_program *shProg,
> struct gl_program *prog)
> {
> GLuint s;
> + struct gl_shader *shader =
> + shProg->_LinkedShaders[_mesa_program_target_to_index(prog->Target)];
>
> - memcpy(prog->SamplerUnits, shProg->SamplerUnits, sizeof(prog->SamplerUnits));
> + assert(shader);
> +
> + memcpy(prog->SamplerUnits, shader->SamplerUnits, sizeof(prog->SamplerUnits));
> memset(prog->TexturesUsed, 0, sizeof(prog->TexturesUsed));
>
> for (s = 0; s < MAX_SAMPLERS; s++) {
> if (prog->SamplersUsed & (1 << s)) {
> - GLuint unit = shProg->SamplerUnits[s];
> - GLuint tgt = shProg->SamplerTargets[s];
> + GLuint unit = shader->SamplerUnits[s];
> + GLuint tgt = shader->SamplerTargets[s];
> assert(unit < Elements(prog->TexturesUsed));
> assert(tgt < NUM_TEXTURE_TARGETS);
> prog->TexturesUsed[unit] |= (1 << tgt);
> diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp
> index 258b864..7e64db5 100644
> --- a/src/mesa/program/ir_to_mesa.cpp
> +++ b/src/mesa/program/ir_to_mesa.cpp
> @@ -2402,8 +2402,10 @@ print_program(struct prog_instruction *mesa_instructions,
> class add_uniform_to_shader : public program_resource_visitor {
> public:
> add_uniform_to_shader(struct gl_shader_program *shader_program,
> - struct gl_program_parameter_list *params)
> - : shader_program(shader_program), params(params), idx(-1)
> + struct gl_program_parameter_list *params,
> + gl_shader_type shader_type)
> + : shader_program(shader_program), params(params), idx(-1),
> + shader_type(shader_type)
> {
> /* empty */
> }
> @@ -2423,6 +2425,7 @@ private:
> struct gl_shader_program *shader_program;
> struct gl_program_parameter_list *params;
> int idx;
> + gl_shader_type shader_type;
> };
>
> void
> @@ -2469,8 +2472,11 @@ add_uniform_to_shader::visit_field(const glsl_type *type, const char *name,
> struct gl_uniform_storage *storage =
> &this->shader_program->UniformStorage[location];
>
> + assert(storage->sampler[shader_type].active);
> +
> for (unsigned int j = 0; j < size / 4; j++)
> - params->ParameterValues[index + j][0].f = storage->sampler + j;
> + params->ParameterValues[index + j][0].f =
> + storage->sampler[shader_type].index + j;
> }
> }
>
> @@ -2496,7 +2502,8 @@ _mesa_generate_parameters_list_for_uniforms(struct gl_shader_program
> struct gl_program_parameter_list
> *params)
> {
> - add_uniform_to_shader add(shader_program, params);
> + add_uniform_to_shader add(shader_program, params,
> + _mesa_shader_type_to_index(sh->Type));
>
> foreach_list(node, sh->ir) {
> ir_variable *var = ((ir_instruction *) node)->as_variable();
> diff --git a/src/mesa/program/sampler.cpp b/src/mesa/program/sampler.cpp
> index e3641aa..1cf3061 100644
> --- a/src/mesa/program/sampler.cpp
> +++ b/src/mesa/program/sampler.cpp
> @@ -34,6 +34,7 @@ extern "C" {
> #include "main/compiler.h"
> #include "main/mtypes.h"
> #include "program/prog_parameter.h"
> +#include "program/program.h"
> }
>
> class get_sampler_name : public ir_hierarchical_visitor
> @@ -102,14 +103,16 @@ public:
> ir_dereference *last;
> };
>
> -extern "C" {
> -int
> +
> +extern "C" int
> _mesa_get_sampler_uniform_value(class ir_dereference *sampler,
> struct gl_shader_program *shader_program,
> const struct gl_program *prog)
> {
> get_sampler_name getname(sampler, shader_program);
>
> + GLuint shader = _mesa_program_target_to_index(prog->Target);
> +
> sampler->accept(&getname);
>
> unsigned location;
> @@ -119,6 +122,14 @@ _mesa_get_sampler_uniform_value(class ir_dereference *sampler,
> return 0;
> }
>
> - return shader_program->UniformStorage[location].sampler + getname.offset;
> -}
> + if (!shader_program->UniformStorage[location].sampler[shader].active) {
> + linker_error(shader_program,
> + "cannot return a sampler named %s, because it is not "
> + "used in this shader stage. This is a driver bug.\n",
> + getname.name);
> + return 0;
> + }
> +
> + return shader_program->UniformStorage[location].sampler[shader].index +
> + getname.offset;
> }
> --
> 1.7.10.4
>
More information about the mesa-dev
mailing list