[Mesa-dev] [PATCH] glsl: Fix program interface queries relating to interface blocks.

Ian Romanick idr at freedesktop.org
Mon Dec 19 21:36:00 UTC 2016


On 12/16/2016 09:35 PM, Kenneth Graunke wrote:
> This fixes 555 dEQP tests (using the nougat-cts-dev branch), Piglit's
> arb_program_interface_query/arb_program_interface_query-resource-query,
> and GL45-CTS.program_interface_query.separate-programs-{tess-control,
> tess-eval,geometry}.  Only one dEQP program interface failure remains.
> 
> I would have liked to split this up into several distinct changes, but
> I wasn't sure how to do that given thet tangled nature of these issues.
> 
> So, the issues:
> 
>    * We need to treat interface blocks declared as an array of instances
>      as a single block - removing the outer array.  The resource list
>      entry's name should not include the array length.  Properties such
>      as GL_ARRAY_SIZE should refer to the variable inside the block, not
>      the interface block's array properties.
> 
>    * We need to do this prefixing even for structure variables.
> 
>    * We need to do this for built-ins (such as gl_PerVertex.gl_Position).
> 
>    * After interface array unwrapping, any variable which is an array
>      should have [0] appended.  It doesn't matter if it's a TCS/TES/GS
>      input or TCS output - that looked like an attempt to unwrap for
>      per-vertex variables, but that didn't consider per-patch variables,
>      and as far as I can tell there's nothing to justify this.
> 
> Several Mesa developers have suggested that Issue 16 contradicts the
> main specification, but I believe that it doesn't - the main spec just
> isn't terribly clear.  The main ARB_program_interface query spec says:
> 
>   "* For an active interface block not declared as an array of block
>      instances, a single entry will be generated, using the block name from
>      the shader source.
> 
>    * For an active interface block declared as an array of instances,
>      separate entries will be generated for each active instance.  The name
>      of the instance is formed by concatenating the block name, the "["
>      character, an integer identifying the instance number, and the "]"
>      character."
> 
> Issue 16 says that built-ins should be named "gl_PerVertex.gl_Position",
> but several people suggested the second bullet above means that it
> should be named "gl_PerVertex[array length].gl_Position".
> 
> There are two important things to note.  Those bullet points say
> "an active interface block", while the others say "variable" or "active
> shader storage block member".  They also don't mention applying the
> rules recursively (unlike the other bullets).  Both suggest that
> these rules apply to blocks themselves, not members of blocks.
> 
> In fact, for GL_UNIFORM_BLOCK queries, we do have "block[0]",
> "block[1]", ... resource list entries - so those rules are real,
> and actually used.  So if they don't apply to block members, then how
> should members be named?  Unfortunately, I don't see any rules outside
> of issue 16 - where the rationale is very unclear.  I hope to clarify
> the spec in the future.

Based on my understanding, something like

out Vertex {
    vec4 p;
    vec4 c[3];
} vertex[2];

in a vertex shader should produce

    Vertex[0].p
    Vertex[0].c[0] (with GL_ARRAY_SIZE = 3)
    Vertex[1].p
    Vertex[1].c[0] (with GL_ARRAY_SIZE = 3)

This is definitely what we would produce for a uniform block.

What I've never understood is why that isn't done for gl_PerVertex stage
boundaries where gl_PerVertex is explicitly arrayed.

I think this patch is good for now.  I like that it brings all the
strange edge-case handling into one place.

Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>

> Cc: Ian Romanick <idr at freedesktop.org>
> Cc: Alejandro PiƱeiro <apinheiro at igalia.com>
> Signed-off-by: Kenneth Graunke <kenneth at whitecape.org>
> ---
>  src/compiler/glsl/linker.cpp   | 100 ++++++++++++++++++++++++-----------------
>  src/mesa/main/shader_query.cpp |  92 ++++++++-----------------------------
>  2 files changed, 79 insertions(+), 113 deletions(-)
> 
> diff --git a/src/compiler/glsl/linker.cpp b/src/compiler/glsl/linker.cpp
> index 5066014..5508d58 100644
> --- a/src/compiler/glsl/linker.cpp
> +++ b/src/compiler/glsl/linker.cpp
> @@ -3735,6 +3735,65 @@ add_shader_variable(const struct gl_context *ctx,
>                      bool use_implicit_location, int location,
>                      const glsl_type *outermost_struct_type = NULL)
>  {
> +   const glsl_type *interface_type = var->get_interface_type();
> +
> +   if (outermost_struct_type == NULL) {
> +      /* Unsized (non-patch) TCS output/TES input arrays are implicitly
> +       * sized to gl_MaxPatchVertices.  Internally, we shrink them to a
> +       * smaller size.
> +       *
> +       * This can cause trouble with SSO programs.  Since the TCS declares
> +       * the number of output vertices, we can always shrink TCS output
> +       * arrays.  However, the TES might not be linked with a TCS, in
> +       * which case it won't know the size of the patch.  In other words,
> +       * the TCS and TES may disagree on the (smaller) array sizes.  This
> +       * can result in the resource names differing across stages, causing
> +       * SSO validation failures and other cascading issues.
> +       *
> +       * Expanding the array size to the full gl_MaxPatchVertices fixes
> +       * these issues.  It's also what program interface queries expect,
> +       * as that is the official size of the array.
> +       */
> +      if (var->data.tess_varying_implicit_sized_array) {
> +         type = resize_to_max_patch_vertices(ctx, type);
> +         interface_type = resize_to_max_patch_vertices(ctx, interface_type);
> +      }
> +
> +      if (var->data.from_named_ifc_block) {
> +         const char *interface_name = interface_type->name;
> +
> +         if (interface_type->is_array()) {
> +            /* Issue #16 of the ARB_program_interface_query spec says:
> +             *
> +             * "* If a variable is a member of an interface block without an
> +             *    instance name, it is enumerated using just the variable name.
> +             *
> +             *  * If a variable is a member of an interface block with an
> +             *    instance name, it is enumerated as "BlockName.Member", where
> +             *    "BlockName" is the name of the interface block (not the
> +             *    instance name) and "Member" is the name of the variable."
> +             *
> +             * In particular, it indicates that it should be "BlockName",
> +             * not "BlockName[array length]".  The conformance suite and
> +             * dEQP both require this behavior.
> +             *
> +             * Here, we unwrap the extra array level added by named interface
> +             * block array lowering so we have the correct variable type.  We
> +             * also unwrap the interface type when constructing the name.
> +             *
> +             * We leave interface_type the same so that ES 3.x SSO pipeline
> +             * validation can enforce the rules requiring array length to
> +             * match on interface blocks.
> +             */
> +            type = type->fields.array;
> +
> +            interface_name = interface_type->fields.array->name;
> +         }
> +
> +         name = ralloc_asprintf(shProg, "%s.%s", interface_name, name);
> +      }
> +   }
> +
>     switch (type->base_type) {
>     case GLSL_TYPE_STRUCT: {
>        /* The ARB_program_interface_query spec says:
> @@ -3766,44 +3825,6 @@ add_shader_variable(const struct gl_context *ctx,
>     }
>  
>     default: {
> -      const glsl_type *interface_type = var->get_interface_type();
> -
> -      /* Unsized (non-patch) TCS output/TES input arrays are implicitly
> -       * sized to gl_MaxPatchVertices.  Internally, we shrink them to a
> -       * smaller size.
> -       *
> -       * This can cause trouble with SSO programs.  Since the TCS declares
> -       * the number of output vertices, we can always shrink TCS output
> -       * arrays.  However, the TES might not be linked with a TCS, in
> -       * which case it won't know the size of the patch.  In other words,
> -       * the TCS and TES may disagree on the (smaller) array sizes.  This
> -       * can result in the resource names differing across stages, causing
> -       * SSO validation failures and other cascading issues.
> -       *
> -       * Expanding the array size to the full gl_MaxPatchVertices fixes
> -       * these issues.  It's also what program interface queries expect,
> -       * as that is the official size of the array.
> -       */
> -      if (var->data.tess_varying_implicit_sized_array) {
> -         type = resize_to_max_patch_vertices(ctx, type);
> -         interface_type = resize_to_max_patch_vertices(ctx, interface_type);
> -      }
> -
> -      /* Issue #16 of the ARB_program_interface_query spec says:
> -       *
> -       * "* If a variable is a member of an interface block without an
> -       *    instance name, it is enumerated using just the variable name.
> -       *
> -       *  * If a variable is a member of an interface block with an instance
> -       *    name, it is enumerated as "BlockName.Member", where "BlockName" is
> -       *    the name of the interface block (not the instance name) and
> -       *    "Member" is the name of the variable."
> -       */
> -      const char *prefixed_name = (var->data.from_named_ifc_block &&
> -                                   !is_gl_identifier(var->name))
> -         ? ralloc_asprintf(shProg, "%s.%s", interface_type->name, name)
> -         : name;
> -
>        /* The ARB_program_interface_query spec says:
>         *
>         *     "For an active variable declared as a single instance of a basic
> @@ -3811,8 +3832,7 @@ add_shader_variable(const struct gl_context *ctx,
>         *     from the shader source."
>         */
>        gl_shader_variable *sha_v =
> -         create_shader_variable(shProg, var, prefixed_name, type,
> -                                interface_type,
> +         create_shader_variable(shProg, var, name, type, interface_type,
>                                  use_implicit_location, location,
>                                  outermost_struct_type);
>        if (!sha_v)
> diff --git a/src/mesa/main/shader_query.cpp b/src/mesa/main/shader_query.cpp
> index e4a8773..1d1616b 100644
> --- a/src/mesa/main/shader_query.cpp
> +++ b/src/mesa/main/shader_query.cpp
> @@ -686,31 +686,14 @@ _mesa_program_resource_find_index(struct gl_shader_program *shProg,
>   * ambiguous in this regard.  However, either name can later be passed
>   * to glGetUniformLocation (and related APIs), so there shouldn't be any
>   * harm in always appending "[0]" to uniform array names.
> - *
> - * Geometry shader stage has different naming convention where the 'normal'
> - * condition is an array, therefore for variables referenced in geometry
> - * stage we do not add '[0]'.
> - *
> - * Note, that TCS outputs and TES inputs should not have index appended
> - * either.
>   */
>  static bool
>  add_index_to_name(struct gl_program_resource *res)
>  {
> -   bool add_index = !((res->Type == GL_PROGRAM_INPUT &&
> -                       res->StageReferences & (1 << MESA_SHADER_GEOMETRY |
> -                                               1 << MESA_SHADER_TESS_CTRL |
> -                                               1 << MESA_SHADER_TESS_EVAL)) ||
> -                      (res->Type == GL_PROGRAM_OUTPUT &&
> -                       res->StageReferences & 1 << MESA_SHADER_TESS_CTRL));
> -
>     /* Transform feedback varyings have array index already appended
>      * in their names.
>      */
> -   if (res->Type == GL_TRANSFORM_FEEDBACK_VARYING)
> -      add_index = false;
> -
> -   return add_index;
> +   return res->Type != GL_TRANSFORM_FEEDBACK_VARYING;
>  }
>  
>  /* Get name length of a program resource. This consists of
> @@ -1401,9 +1384,6 @@ validate_io(struct gl_shader_program *producer,
>  
>     bool valid = true;
>  
> -   void *name_buffer = NULL;
> -   size_t name_buffer_size = 0;
> -
>     gl_shader_variable const **outputs =
>        (gl_shader_variable const **) calloc(producer->NumProgramResourceList,
>                                             sizeof(gl_shader_variable *));
> @@ -1475,52 +1455,11 @@ validate_io(struct gl_shader_program *producer,
>              }
>           }
>        } else {
> -         char *consumer_name = consumer_var->name;
> -
> -         if (nonarray_stage_to_array_stage &&
> -             consumer_var->interface_type != NULL &&
> -             consumer_var->interface_type->is_array() &&
> -             !is_gl_identifier(consumer_var->name)) {
> -            const size_t name_len = strlen(consumer_var->name);
> -
> -            if (name_len >= name_buffer_size) {
> -               free(name_buffer);
> -
> -               name_buffer_size = name_len + 1;
> -               name_buffer = malloc(name_buffer_size);
> -               if (name_buffer == NULL) {
> -                  valid = false;
> -                  goto out;
> -               }
> -            }
> -
> -            consumer_name = (char *) name_buffer;
> -
> -            char *s = strchr(consumer_var->name, '[');
> -            if (s == NULL) {
> -               valid = false;
> -               goto out;
> -            }
> -
> -            char *t = strchr(s, ']');
> -            if (t == NULL) {
> -               valid = false;
> -               goto out;
> -            }
> -
> -            assert(t[1] == '.' || t[1] == '[');
> -
> -            const ptrdiff_t base_name_len = s - consumer_var->name;
> -
> -            memcpy(consumer_name, consumer_var->name, base_name_len);
> -            strcpy(consumer_name + base_name_len, t + 1);
> -         }
> -
>           for (unsigned j = 0; j < num_outputs; j++) {
>              const gl_shader_variable *const var = outputs[j];
>  
>              if (!var->explicit_location &&
> -                strcmp(consumer_name, var->name) == 0) {
> +                strcmp(consumer_var->name, var->name) == 0) {
>                 producer_var = var;
>                 match_index = j;
>                 break;
> @@ -1583,21 +1522,29 @@ validate_io(struct gl_shader_program *producer,
>         * find the producer variable that goes with the consumer variable.
>         */
>        if (nonarray_stage_to_array_stage) {
> -         if (!consumer_var->type->is_array() ||
> -             consumer_var->type->fields.array != producer_var->type) {
> -            valid = false;
> -            goto out;
> -         }
> -
>           if (consumer_var->interface_type != NULL) {
> +            /* the interface is the array; underlying types should match */
> +            if (producer_var->type != consumer_var->type) {
> +               valid = false;
> +               goto out;
> +            }
> +
>              if (!consumer_var->interface_type->is_array() ||
>                  consumer_var->interface_type->fields.array != producer_var->interface_type) {
>                 valid = false;
>                 goto out;
>              }
> -         } else if (producer_var->interface_type != NULL) {
> -            valid = false;
> -            goto out;
> +         } else {
> +            if (producer_var->interface_type != NULL) {
> +               valid = false;
> +               goto out;
> +            }
> +
> +            if (!consumer_var->type->is_array() ||
> +                consumer_var->type->fields.array != producer_var->type) {
> +               valid = false;
> +               goto out;
> +            }
>           }
>        } else {
>           if (producer_var->type != consumer_var->type) {
> @@ -1628,7 +1575,6 @@ validate_io(struct gl_shader_program *producer,
>     }
>  
>   out:
> -   free(name_buffer);
>     free(outputs);
>     return valid && num_outputs == 0;
>  }
> 



More information about the mesa-dev mailing list