From 9b477ad49d3f82503a1b8ba23dedfc05cd848fe8 Mon Sep 17 00:00:00 2001 From: Samuel Iglesias Gonsalvez Date: Wed, 6 May 2015 08:11:02 +0200 Subject: [PATCH] main: Add SHADER_STORAGE_BLOCK and BUFFER_VARIABLE support for ARB_program_interface_query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Including TOP_LEVEL_ARRAY_SIZE and TOP_LEVEL_ARRAY_STRIDE queries. v2: - Use std430_array_stride() to get top level array stride following std430's rules. Signed-off-by: Samuel Iglesias Gonsalvez Reviewed-by: Tapani Pälli Reviewed-by: Kristian Høgsberg --- src/glsl/ir_uniform.h | 5 + src/glsl/link_uniforms.cpp | 3 + src/glsl/linker.cpp | 10 +- src/mesa/main/program_resource.c | 7 +- src/mesa/main/shader_query.cpp | 265 ++++++++++++++++++++++++++++++- 5 files changed, 278 insertions(+), 12 deletions(-) diff --git a/src/glsl/ir_uniform.h b/src/glsl/ir_uniform.h index 0b6f7201a20..858a7da6bb9 100644 --- a/src/glsl/ir_uniform.h +++ b/src/glsl/ir_uniform.h @@ -194,6 +194,11 @@ struct gl_uniform_storage { * This is a built-in uniform that should not be modified through any gl API. */ bool builtin; + + /** + * This is a shader storage buffer variable, not an uniform. + */ + bool is_shader_storage; }; #ifdef __cplusplus diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp index 50a80732d73..1c901e2cecb 100644 --- a/src/glsl/link_uniforms.cpp +++ b/src/glsl/link_uniforms.cpp @@ -794,6 +794,9 @@ private: if (!this->uniforms[id].builtin) this->uniforms[id].storage = this->values; + this->uniforms[id].is_shader_storage = + current_var->is_in_shader_storage_block(); + if (this->ubo_block_index != -1) { this->uniforms[id].block_index = this->ubo_block_index; diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index aebf2560dab..9d419ac9d39 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -3406,14 +3406,18 @@ build_program_resource_list(struct gl_shader_program *shProg) } } - if (!add_program_resource(shProg, GL_UNIFORM, + bool is_shader_storage = shProg->UniformStorage[i].is_shader_storage; + GLenum type = is_shader_storage ? GL_BUFFER_VARIABLE : GL_UNIFORM; + if (!add_program_resource(shProg, type, &shProg->UniformStorage[i], stageref)) return; } - /* Add program uniform blocks. */ + /* Add program uniform blocks and shader storage blocks. */ for (unsigned i = 0; i < shProg->NumUniformBlocks; i++) { - if (!add_program_resource(shProg, GL_UNIFORM_BLOCK, + bool is_shader_storage = shProg->UniformBlocks[i].IsShaderStorage; + GLenum type = is_shader_storage ? GL_SHADER_STORAGE_BLOCK : GL_UNIFORM_BLOCK; + if (!add_program_resource(shProg, type, &shProg->UniformBlocks[i], 0)) return; } diff --git a/src/mesa/main/program_resource.c b/src/mesa/main/program_resource.c index 23d2b4d2da0..c609abeed45 100644 --- a/src/mesa/main/program_resource.c +++ b/src/mesa/main/program_resource.c @@ -41,6 +41,8 @@ supported_interface_enum(struct gl_context *ctx, GLenum iface) case GL_PROGRAM_OUTPUT: case GL_TRANSFORM_FEEDBACK_VARYING: case GL_ATOMIC_COUNTER_BUFFER: + case GL_BUFFER_VARIABLE: + case GL_SHADER_STORAGE_BLOCK: return true; case GL_VERTEX_SUBROUTINE: case GL_FRAGMENT_SUBROUTINE: @@ -58,8 +60,6 @@ supported_interface_enum(struct gl_context *ctx, GLenum iface) case GL_TESS_CONTROL_SUBROUTINE_UNIFORM: case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: return _mesa_has_tessellation(ctx) && _mesa_has_shader_subroutine(ctx); - case GL_BUFFER_VARIABLE: - case GL_SHADER_STORAGE_BLOCK: default: return false; } @@ -121,6 +121,7 @@ _mesa_GetProgramInterfaceiv(GLuint program, GLenum programInterface, case GL_MAX_NUM_ACTIVE_VARIABLES: switch (programInterface) { case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) { if (shProg->ProgramResourceList[i].Type == programInterface) { struct gl_uniform_block *block = @@ -247,8 +248,10 @@ _mesa_GetProgramResourceIndex(GLuint program, GLenum programInterface, case GL_PROGRAM_INPUT: case GL_PROGRAM_OUTPUT: case GL_UNIFORM: + case GL_BUFFER_VARIABLE: case GL_TRANSFORM_FEEDBACK_VARYING: case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: res = _mesa_program_resource_find_name(shProg, programInterface, name, &array_index); if (!res || array_index > 0) diff --git a/src/mesa/main/shader_query.cpp b/src/mesa/main/shader_query.cpp index ee7320221e2..99d9e1088d0 100644 --- a/src/mesa/main/shader_query.cpp +++ b/src/mesa/main/shader_query.cpp @@ -431,6 +431,7 @@ _mesa_program_resource_name(struct gl_program_resource *res) const ir_variable *var; switch (res->Type) { case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: return RESOURCE_UBO(res)->Name; case GL_TRANSFORM_FEEDBACK_VARYING: return RESOURCE_XFB(res)->Name; @@ -445,6 +446,7 @@ _mesa_program_resource_name(struct gl_program_resource *res) case GL_PROGRAM_OUTPUT: return RESOURCE_VAR(res)->name; case GL_UNIFORM: + case GL_BUFFER_VARIABLE: return RESOURCE_UNI(res)->name; case GL_VERTEX_SUBROUTINE_UNIFORM: case GL_GEOMETRY_SUBROUTINE_UNIFORM: @@ -484,6 +486,7 @@ _mesa_program_resource_array_size(struct gl_program_resource *res) case GL_COMPUTE_SUBROUTINE_UNIFORM: case GL_TESS_CONTROL_SUBROUTINE_UNIFORM: case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM: + case GL_BUFFER_VARIABLE: return RESOURCE_UNI(res)->array_elements; case GL_VERTEX_SUBROUTINE: case GL_GEOMETRY_SUBROUTINE: @@ -493,6 +496,7 @@ _mesa_program_resource_array_size(struct gl_program_resource *res) case GL_TESS_EVALUATION_SUBROUTINE: case GL_ATOMIC_COUNTER_BUFFER: case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: return 0; default: assert(!"support for resource type not implemented"); @@ -538,6 +542,7 @@ _mesa_program_resource_find_name(struct gl_shader_program *shProg, if (strncmp(rname, name, baselen) == 0) { switch (programInterface) { case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: /* Basename match, check if array or struct. */ if (name[baselen] == '\0' || name[baselen] == '[' || @@ -546,6 +551,7 @@ _mesa_program_resource_find_name(struct gl_shader_program *shProg, } break; case GL_TRANSFORM_FEEDBACK_VARYING: + case GL_BUFFER_VARIABLE: case GL_UNIFORM: case GL_VERTEX_SUBROUTINE_UNIFORM: case GL_GEOMETRY_SUBROUTINE_UNIFORM: @@ -607,6 +613,7 @@ _mesa_program_resource_index(struct gl_shader_program *shProg, switch (res->Type) { case GL_UNIFORM_BLOCK: + case GL_SHADER_STORAGE_BLOCK: return RESOURCE_UBO(res)- shProg->UniformBlocks; case GL_ATOMIC_COUNTER_BUFFER: return RESOURCE_ATC(res) - shProg->AtomicBuffers; @@ -632,6 +639,7 @@ _mesa_program_resource_find_index(struct gl_shader_program *shProg, switch (res->Type) { case GL_UNIFORM_BLOCK: case GL_ATOMIC_COUNTER_BUFFER: + case GL_SHADER_STORAGE_BLOCK: if (_mesa_program_resource_index(shProg, res) == index) return res; break; @@ -651,6 +659,7 @@ _mesa_program_resource_find_index(struct gl_shader_program *shProg, case GL_COMPUTE_SUBROUTINE: case GL_TESS_CONTROL_SUBROUTINE: case GL_TESS_EVALUATION_SUBROUTINE: + case GL_BUFFER_VARIABLE: if (++idx == (int) index) return res; break; @@ -804,6 +813,192 @@ program_resource_location(struct gl_shader_program *shProg, } } +static char* +get_top_level_name(const char *name) +{ + const char *first_dot = strchr(name, '.'); + const char *first_square_bracket = strchr(name, '['); + int name_size = 0; + /* From ARB_program_interface_query spec: + * + * "For the property TOP_LEVEL_ARRAY_SIZE, a single integer identifying the + * number of active array elements of the top-level shader storage block + * member containing to the active variable is written to . If the + * top-level block member is not declared as an array, the value one is + * written to . If the top-level block member is an array with no + * declared size, the value zero is written to . + */ + + /* The buffer variable is on top level.*/ + if (!first_square_bracket && !first_dot) + name_size = strlen(name); + else if ((!first_square_bracket || + (first_dot && first_dot < first_square_bracket))) + name_size = first_dot - name; + else + name_size = first_square_bracket - name; + + return strndup(name, name_size); +} + +static char* +get_var_name(const char *name) +{ + const char *first_dot = strchr(name, '.'); + + if (!first_dot) + return strdup(name); + + return strndup(first_dot+1, strlen(first_dot) - 1); +} + +static GLint +program_resource_top_level_array_size(struct gl_shader_program *shProg, + struct gl_program_resource *res, + const char *name) +{ + int block_index = RESOURCE_UNI(res)->block_index; + int array_size = -1; + char *var_name = get_top_level_name(name); + char *interface_name = + get_top_level_name(shProg->UniformBlocks[block_index].Name); + + if (strcmp(var_name, interface_name) == 0) { + /* Deal with instanced array of SSBOs */ + char *temp_name = get_var_name(name); + free(var_name); + var_name = get_top_level_name(temp_name); + free(temp_name); + } + + for (unsigned i = 0; i < shProg->NumShaders; i++) { + if (shProg->Shaders[i] == NULL) + continue; + + const gl_shader *stage = shProg->Shaders[i]; + foreach_in_list(ir_instruction, node, stage->ir) { + ir_variable *var = node->as_variable(); + if (!var || !var->get_interface_type() || + var->data.mode != ir_var_shader_storage) + continue; + + const glsl_type *interface = var->get_interface_type(); + + if (strcmp(interface_name, interface->name) != 0) + continue; + + for (unsigned i = 0; i < interface->length; i++) { + const glsl_struct_field *field = &interface->fields.structure[i]; + if (strcmp(field->name, var_name) != 0) + continue; + /* From GL_ARB_program_interface_query spec: + * + * "For the property TOP_LEVEL_ARRAY_SIZE, a single integer + * identifying the number of active array elements of the top-level + * shader storage block member containing to the active variable is + * written to . If the top-level block member is not + * declared as an array, the value one is written to . If + * the top-level block member is an array with no declared size, + * the value zero is written to . + */ + if (field->type->is_unsized_array()) + array_size = 0; + else if (field->type->is_array()) + array_size = field->type->length; + else + array_size = 1; + goto found_top_level_array_size; + } + } + } +found_top_level_array_size: + free(interface_name); + free(var_name); + return array_size; +} + +static GLint +program_resource_top_level_array_stride(struct gl_shader_program *shProg, + struct gl_program_resource *res, + const char *name) +{ + int block_index = RESOURCE_UNI(res)->block_index; + int array_stride = -1; + char *var_name = get_top_level_name(name); + char *interface_name = + get_top_level_name(shProg->UniformBlocks[block_index].Name); + + if (strcmp(var_name, interface_name) == 0) { + /* Deal with instanced array of SSBOs */ + char *temp_name = get_var_name(name); + free(var_name); + var_name = get_top_level_name(temp_name); + free(temp_name); + } + + for (unsigned i = 0; i < shProg->NumShaders; i++) { + if (shProg->Shaders[i] == NULL) + continue; + + const gl_shader *stage = shProg->Shaders[i]; + foreach_in_list(ir_instruction, node, stage->ir) { + ir_variable *var = node->as_variable(); + if (!var || !var->get_interface_type() || + var->data.mode != ir_var_shader_storage) + continue; + + const glsl_type *interface = var->get_interface_type(); + + if (strcmp(interface_name, interface->name) != 0) { + continue; + } + + for (unsigned i = 0; i < interface->length; i++) { + const glsl_struct_field *field = &interface->fields.structure[i]; + if (strcmp(field->name, var_name) != 0) + continue; + /* From GL_ARB_program_interface_query: + * + * "For the property TOP_LEVEL_ARRAY_STRIDE, a single integer + * identifying the stride between array elements of the top-level + * shader storage block member containing the active variable is + * written to . For top-level block members declared as + * arrays, the value written is the difference, in basic machine + * units, between the offsets of the active variable for + * consecutive elements in the top-level array. For top-level + * block members not declared as an array, zero is written to + * ." + */ + if (field->type->is_array()) { + const enum glsl_matrix_layout matrix_layout = + glsl_matrix_layout(field->matrix_layout); + bool row_major = matrix_layout == GLSL_MATRIX_LAYOUT_ROW_MAJOR; + const glsl_type *array_type = field->type->fields.array; + + if (interface->interface_packing != GLSL_INTERFACE_PACKING_STD430) { + if (array_type->is_record()) { + array_stride = array_type->std140_size(row_major); + array_stride = glsl_align(array_stride, 16); + } else { + unsigned element_base_align = 0; + element_base_align = array_type->std140_base_alignment(row_major); + array_stride = MAX2(element_base_align, 16); + } + } else { + array_stride = array_type->std430_array_stride(row_major); + } + } else { + array_stride = 0; + } + goto found_top_level_array_size; + } + } + } +found_top_level_array_size: + free(var_name); + return array_stride; +} + /** * Function implements following location queries: * glGetUniformLocation @@ -880,7 +1075,7 @@ is_resource_referenced(struct gl_shader_program *shProg, if (res->Type == GL_ATOMIC_COUNTER_BUFFER) return RESOURCE_ATC(res)->StageReferences[stage]; - if (res->Type == GL_UNIFORM_BLOCK) + if (res->Type == GL_UNIFORM_BLOCK || res->Type == GL_SHADER_STORAGE_BLOCK) return shProg->UniformBlockStageIndex[stage][index] != -1; return res->StageReferences & (1 << stage); @@ -893,7 +1088,8 @@ get_buffer_property(struct gl_shader_program *shProg, { GET_CURRENT_CONTEXT(ctx); if (res->Type != GL_UNIFORM_BLOCK && - res->Type != GL_ATOMIC_COUNTER_BUFFER) + res->Type != GL_ATOMIC_COUNTER_BUFFER && + res->Type != GL_SHADER_STORAGE_BLOCK) goto invalid_operation; if (res->Type == GL_UNIFORM_BLOCK) { @@ -929,6 +1125,39 @@ get_buffer_property(struct gl_shader_program *shProg, } return RESOURCE_UBO(res)->NumUniforms; } + } else if (res->Type == GL_SHADER_STORAGE_BLOCK) { + switch (prop) { + case GL_BUFFER_BINDING: + *val = RESOURCE_UBO(res)->Binding; + return 1; + case GL_BUFFER_DATA_SIZE: + *val = RESOURCE_UBO(res)->UniformBufferSize; + return 1; + case GL_NUM_ACTIVE_VARIABLES: + *val = 0; + for (unsigned i = 0; i < RESOURCE_UBO(res)->NumUniforms; i++) { + const char *iname = RESOURCE_UBO(res)->Uniforms[i].IndexName; + struct gl_program_resource *uni = + _mesa_program_resource_find_name(shProg, GL_BUFFER_VARIABLE, + iname, NULL); + if (!uni) + continue; + (*val)++; + } + return 1; + case GL_ACTIVE_VARIABLES: + for (unsigned i = 0; i < RESOURCE_UBO(res)->NumUniforms; i++) { + const char *iname = RESOURCE_UBO(res)->Uniforms[i].IndexName; + struct gl_program_resource *uni = + _mesa_program_resource_find_name(shProg, GL_BUFFER_VARIABLE, + iname, NULL); + if (!uni) + continue; + *val++ = + _mesa_program_resource_index(shProg, uni); + } + return RESOURCE_UBO(res)->NumUniforms; + } } else if (res->Type == GL_ATOMIC_COUNTER_BUFFER) { switch (prop) { case GL_BUFFER_BINDING: @@ -967,6 +1196,10 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, if (res->Type != type)\ goto invalid_operation; +#define VALIDATE_TYPE_2(type1, type2)\ + if (res->Type != type1 && res->Type != type2)\ + goto invalid_operation; + switch(prop) { case GL_NAME_LENGTH: switch (res->Type) { @@ -984,6 +1217,7 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, case GL_TYPE: switch (res->Type) { case GL_UNIFORM: + case GL_BUFFER_VARIABLE: *val = RESOURCE_UNI(res)->type->gl_type; return 1; case GL_PROGRAM_INPUT: @@ -999,6 +1233,7 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, case GL_ARRAY_SIZE: switch (res->Type) { case GL_UNIFORM: + case GL_BUFFER_VARIABLE: *val = MAX2(RESOURCE_UNI(res)->array_elements, 1); return 1; case GL_PROGRAM_INPUT: @@ -1012,23 +1247,23 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, goto invalid_operation; } case GL_OFFSET: - VALIDATE_TYPE(GL_UNIFORM); + VALIDATE_TYPE_2(GL_UNIFORM, GL_BUFFER_VARIABLE); *val = RESOURCE_UNI(res)->offset; return 1; case GL_BLOCK_INDEX: - VALIDATE_TYPE(GL_UNIFORM); + VALIDATE_TYPE_2(GL_UNIFORM, GL_BUFFER_VARIABLE); *val = RESOURCE_UNI(res)->block_index; return 1; case GL_ARRAY_STRIDE: - VALIDATE_TYPE(GL_UNIFORM); + VALIDATE_TYPE_2(GL_UNIFORM, GL_BUFFER_VARIABLE); *val = RESOURCE_UNI(res)->array_stride; return 1; case GL_MATRIX_STRIDE: - VALIDATE_TYPE(GL_UNIFORM); + VALIDATE_TYPE_2(GL_UNIFORM, GL_BUFFER_VARIABLE); *val = RESOURCE_UNI(res)->matrix_stride; return 1; case GL_IS_ROW_MAJOR: - VALIDATE_TYPE(GL_UNIFORM); + VALIDATE_TYPE_2(GL_UNIFORM, GL_BUFFER_VARIABLE); *val = RESOURCE_UNI(res)->row_major; return 1; case GL_ATOMIC_COUNTER_BUFFER_INDEX: @@ -1054,6 +1289,8 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, case GL_PROGRAM_INPUT: case GL_PROGRAM_OUTPUT: case GL_UNIFORM_BLOCK: + case GL_BUFFER_VARIABLE: + case GL_SHADER_STORAGE_BLOCK: case GL_ATOMIC_COUNTER_BUFFER: *val = is_resource_referenced(shProg, res, index, stage_from_enum(prop)); @@ -1117,6 +1354,19 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, } return count; } + + case GL_TOP_LEVEL_ARRAY_SIZE: + VALIDATE_TYPE(GL_BUFFER_VARIABLE); + *val = program_resource_top_level_array_size(shProg, res, + _mesa_program_resource_name(res)); + return 1; + + case GL_TOP_LEVEL_ARRAY_STRIDE: + VALIDATE_TYPE(GL_BUFFER_VARIABLE); + *val = program_resource_top_level_array_stride(shProg, res, + _mesa_program_resource_name(res)); + return 1; + /* GL_ARB_tessellation_shader */ case GL_IS_PER_PATCH: switch (res->Type) { @@ -1132,6 +1382,7 @@ _mesa_program_resource_prop(struct gl_shader_program *shProg, } #undef VALIDATE_TYPE +#undef VALIDATE_TYPE_2 invalid_enum: _mesa_error(ctx, GL_INVALID_ENUM, "%s(%s prop %s)", caller,