From c796ce4108ccc4987c24df43606d04a0f3658d44 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tapani=20P=C3=A4lli?= Date: Fri, 6 Mar 2015 09:14:49 +0200 Subject: [PATCH] mesa/glsl: build list of program resources during linking MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Patch adds ProgramResourceList to gl_shader_program structure. List contains references to active program resources and is constructed during linking phase. This list will be used by follow-up patches to implement hooks for GL_ARB_program_interface_query. It can be also used to implement any of the older shader program query APIs. v2: code cleanups + note for SSBO and subroutines (Ilia Mirkin) v3: code cleanups + assert(MESA_SHADER_STAGES < 8) (Martin Peres) Signed-off-by: Tapani Pälli Reviewed-by: Martin Peres --- src/glsl/linker.cpp | 192 ++++++++++++++++++++++++++++++++++++++ src/mesa/main/mtypes.h | 14 +++ src/mesa/main/shaderobj.c | 6 ++ 3 files changed, 212 insertions(+) diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index 118614bdd80..651ecd85a15 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -2492,6 +2492,194 @@ check_explicit_uniform_locations(struct gl_context *ctx, delete uniform_map; } +static bool +add_program_resource(struct gl_shader_program *prog, GLenum type, + const void *data, uint8_t stages) +{ + assert(data); + + /* If resource already exists, do not add it again. */ + for (unsigned i = 0; i < prog->NumProgramResourceList; i++) + if (prog->ProgramResourceList[i].Data == data) + return true; + + prog->ProgramResourceList = + reralloc(prog, + prog->ProgramResourceList, + gl_program_resource, + prog->NumProgramResourceList + 1); + + if (!prog->ProgramResourceList) { + linker_error(prog, "Out of memory during linking.\n"); + return false; + } + + struct gl_program_resource *res = + &prog->ProgramResourceList[prog->NumProgramResourceList]; + + res->Type = type; + res->Data = data; + res->StageReferences = stages; + + prog->NumProgramResourceList++; + + return true; +} + +/** + * Function builds a stage reference bitmask from variable name. + */ +static uint8_t +build_stageref(struct gl_shader_program *shProg, const char *name) +{ + uint8_t stages = 0; + + /* Note, that we assume MAX 8 stages, if there will be more stages, type + * used for reference mask in gl_program_resource will need to be changed. + */ + assert(MESA_SHADER_STAGES < 8); + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_shader *sh = shProg->_LinkedShaders[i]; + if (!sh) + continue; + ir_variable *var = sh->symbols->get_variable(name); + if (var) + stages |= (1 << i); + } + return stages; +} + +static bool +add_interface_variables(struct gl_shader_program *shProg, + struct gl_shader *sh, GLenum interface) +{ + foreach_in_list(ir_instruction, node, sh->ir) { + ir_variable *var = node->as_variable(); + + if (!var) + continue; + + switch (var->data.mode) { + /* From GL 4.3 core spec, section 11.1.1 (Vertex Attributes): + * "For GetActiveAttrib, all active vertex shader input variables + * are enumerated, including the special built-in inputs gl_VertexID + * and gl_InstanceID." + */ + case ir_var_system_value: + if (var->data.location != SYSTEM_VALUE_VERTEX_ID && + var->data.location != SYSTEM_VALUE_VERTEX_ID_ZERO_BASE && + var->data.location != SYSTEM_VALUE_INSTANCE_ID) + continue; + case ir_var_shader_in: + if (interface != GL_PROGRAM_INPUT) + continue; + break; + case ir_var_shader_out: + if (interface != GL_PROGRAM_OUTPUT) + continue; + break; + default: + continue; + }; + + if (!add_program_resource(shProg, interface, var, + build_stageref(shProg, var->name))) + return false; + } + return true; +} + +/** + * Builds up a list of program resources that point to existing + * resource data. + */ +static void +build_program_resource_list(struct gl_context *ctx, + struct gl_shader_program *shProg) +{ + /* Rebuild resource list. */ + if (shProg->ProgramResourceList) { + ralloc_free(shProg->ProgramResourceList); + shProg->ProgramResourceList = NULL; + shProg->NumProgramResourceList = 0; + } + + int input_stage = MESA_SHADER_STAGES, output_stage = 0; + + /* Determine first input and final output stage. These are used to + * detect which variables should be enumerated in the resource list + * for GL_PROGRAM_INPUT and GL_PROGRAM_OUTPUT. + */ + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (!shProg->_LinkedShaders[i]) + continue; + if (input_stage == MESA_SHADER_STAGES) + input_stage = i; + output_stage = i; + } + + /* Empty shader, no resources. */ + if (input_stage == MESA_SHADER_STAGES && output_stage == 0) + return; + + /* Add inputs and outputs to the resource list. */ + if (!add_interface_variables(shProg, shProg->_LinkedShaders[input_stage], + GL_PROGRAM_INPUT)) + return; + + if (!add_interface_variables(shProg, shProg->_LinkedShaders[output_stage], + GL_PROGRAM_OUTPUT)) + return; + + /* Add transform feedback varyings. */ + if (shProg->LinkedTransformFeedback.NumVarying > 0) { + for (int i = 0; i < shProg->LinkedTransformFeedback.NumVarying; i++) { + uint8_t stageref = + build_stageref(shProg, + shProg->LinkedTransformFeedback.Varyings[i].Name); + if (!add_program_resource(shProg, GL_TRANSFORM_FEEDBACK_VARYING, + &shProg->LinkedTransformFeedback.Varyings[i], + stageref)) + return; + } + } + + /* Add uniforms from uniform storage. */ + for (unsigned i = 0; i < shProg->NumUserUniformStorage; i++) { + /* Do not add uniforms internally used by Mesa. */ + if (shProg->UniformStorage[i].hidden) + continue; + + uint8_t stageref = + build_stageref(shProg, shProg->UniformStorage[i].name); + if (!add_program_resource(shProg, GL_UNIFORM, + &shProg->UniformStorage[i], stageref)) + return; + } + + /* Add program uniform blocks. */ + for (unsigned i = 0; i < shProg->NumUniformBlocks; i++) { + if (!add_program_resource(shProg, GL_UNIFORM_BLOCK, + &shProg->UniformBlocks[i], 0)) + return; + } + + /* Add atomic counter buffers. */ + for (unsigned i = 0; i < shProg->NumAtomicBuffers; i++) { + if (!add_program_resource(shProg, GL_ATOMIC_COUNTER_BUFFER, + &shProg->AtomicBuffers[i], 0)) + return; + } + + /* TODO - following extensions will require more resource types: + * + * GL_ARB_shader_storage_buffer_object + * GL_ARB_shader_subroutine + */ +} + + void link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) { @@ -2899,6 +3087,10 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) } } + build_program_resource_list(ctx, prog); + if (!prog->LinkStatus) + goto done; + /* FINISHME: Assign fragment shader output locations. */ done: diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index 56d3b7e1e89..1c751cfff25 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -2603,6 +2603,16 @@ struct gl_active_atomic_buffer GLboolean StageReferences[MESA_SHADER_STAGES]; }; +/** + * Active resource in a gl_shader_program + */ +struct gl_program_resource +{ + GLenum Type; /** Program interface type. */ + const void *Data; /** Pointer to resource associated data structure. */ + uint8_t StageReferences; /** Bitmask of shader stage references. */ +}; + /** * A GLSL program object. * Basically a linked collection of vertex and fragment shaders. @@ -2777,6 +2787,10 @@ struct gl_shader_program */ struct gl_shader *_LinkedShaders[MESA_SHADER_STAGES]; + /** List of all active resources after linking. */ + struct gl_program_resource *ProgramResourceList; + unsigned NumProgramResourceList; + /* True if any of the fragment shaders attached to this program use: * #extension ARB_fragment_coord_conventions: enable */ diff --git a/src/mesa/main/shaderobj.c b/src/mesa/main/shaderobj.c index d7620c8ef7e..e428960362d 100644 --- a/src/mesa/main/shaderobj.c +++ b/src/mesa/main/shaderobj.c @@ -315,6 +315,12 @@ _mesa_clear_shader_program_data(struct gl_shader_program *shProg) ralloc_free(shProg->AtomicBuffers); shProg->AtomicBuffers = NULL; shProg->NumAtomicBuffers = 0; + + if (shProg->ProgramResourceList) { + ralloc_free(shProg->ProgramResourceList); + shProg->ProgramResourceList = NULL; + shProg->NumProgramResourceList = 0; + } } -- 2.30.2