/* * Copyright © 2017 Ilia Mirkin * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file lower_cs_derived.cpp * * For hardware that does not support the gl_GlobalInvocationID and * gl_LocalInvocationIndex system values, replace them with fresh * globals. Note that we can't rely on gl_WorkGroupSize or * gl_LocalGroupSizeARB being available, since they may only have been defined * in a non-main shader. * * [ This can happen if only a secondary shader has the layout(local_size_*) * declaration. ] * * This is meant to be run post-linking. */ #include "glsl_symbol_table.h" #include "ir_hierarchical_visitor.h" #include "ir.h" #include "ir_builder.h" #include "linker.h" #include "program/prog_statevars.h" #include "builtin_functions.h" using namespace ir_builder; namespace { class lower_cs_derived_visitor : public ir_hierarchical_visitor { public: explicit lower_cs_derived_visitor(gl_linked_shader *shader) : progress(false), shader(shader), local_size_variable(shader->Program->info.cs.local_size_variable), gl_WorkGroupSize(NULL), gl_WorkGroupID(NULL), gl_LocalInvocationID(NULL), gl_GlobalInvocationID(NULL), gl_LocalInvocationIndex(NULL) { main_sig = _mesa_get_main_function_signature(shader->symbols); assert(main_sig); } virtual ir_visitor_status visit(ir_dereference_variable *); ir_variable *add_system_value( int slot, const glsl_type *type, const char *name); void find_sysvals(); void make_gl_GlobalInvocationID(); void make_gl_LocalInvocationIndex(); bool progress; private: gl_linked_shader *shader; bool local_size_variable; ir_function_signature *main_sig; ir_rvalue *gl_WorkGroupSize; ir_variable *gl_WorkGroupID; ir_variable *gl_LocalInvocationID; ir_variable *gl_GlobalInvocationID; ir_variable *gl_LocalInvocationIndex; }; } /* anonymous namespace */ ir_variable * lower_cs_derived_visitor::add_system_value( int slot, const glsl_type *type, const char *name) { ir_variable *var = new(shader) ir_variable(type, name, ir_var_system_value); var->data.how_declared = ir_var_declared_implicitly; var->data.read_only = true; var->data.location = slot; var->data.explicit_location = true; var->data.explicit_index = 0; shader->ir->push_head(var); return var; } void lower_cs_derived_visitor::find_sysvals() { if (gl_WorkGroupSize != NULL) return; ir_variable *WorkGroupSize; if (local_size_variable) WorkGroupSize = shader->symbols->get_variable("gl_LocalGroupSizeARB"); else WorkGroupSize = shader->symbols->get_variable("gl_WorkGroupSize"); if (WorkGroupSize) gl_WorkGroupSize = new(shader) ir_dereference_variable(WorkGroupSize); gl_WorkGroupID = shader->symbols->get_variable("gl_WorkGroupID"); gl_LocalInvocationID = shader->symbols->get_variable("gl_LocalInvocationID"); /* * These may be missing due to either dead code elimination, or, in the * case of the group size, due to the layout being declared in a non-main * shader. Re-create them. */ if (!gl_WorkGroupID) gl_WorkGroupID = add_system_value( SYSTEM_VALUE_WORK_GROUP_ID, glsl_type::uvec3_type, "gl_WorkGroupID"); if (!gl_LocalInvocationID) gl_LocalInvocationID = add_system_value( SYSTEM_VALUE_LOCAL_INVOCATION_ID, glsl_type::uvec3_type, "gl_LocalInvocationID"); if (!WorkGroupSize) { if (local_size_variable) { gl_WorkGroupSize = new(shader) ir_dereference_variable( add_system_value( SYSTEM_VALUE_LOCAL_GROUP_SIZE, glsl_type::uvec3_type, "gl_LocalGroupSizeARB")); } else { ir_constant_data data; memset(&data, 0, sizeof(data)); for (int i = 0; i < 3; i++) data.u[i] = shader->Program->info.cs.local_size[i]; gl_WorkGroupSize = new(shader) ir_constant(glsl_type::uvec3_type, &data); } } } void lower_cs_derived_visitor::make_gl_GlobalInvocationID() { if (gl_GlobalInvocationID != NULL) return; find_sysvals(); /* gl_GlobalInvocationID = * gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID */ gl_GlobalInvocationID = new(shader) ir_variable( glsl_type::uvec3_type, "__GlobalInvocationID", ir_var_temporary); shader->ir->push_head(gl_GlobalInvocationID); ir_instruction *inst = assign(gl_GlobalInvocationID, add(mul(gl_WorkGroupID, gl_WorkGroupSize->clone(shader, NULL)), gl_LocalInvocationID)); main_sig->body.push_head(inst); } void lower_cs_derived_visitor::make_gl_LocalInvocationIndex() { if (gl_LocalInvocationIndex != NULL) return; find_sysvals(); /* gl_LocalInvocationIndex = * gl_LocalInvocationID.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + * gl_LocalInvocationID.y * gl_WorkGroupSize.x + * gl_LocalInvocationID.x; */ gl_LocalInvocationIndex = new(shader) ir_variable(glsl_type::uint_type, "__LocalInvocationIndex", ir_var_temporary); shader->ir->push_head(gl_LocalInvocationIndex); ir_expression *index_z = mul(mul(swizzle_z(gl_LocalInvocationID), swizzle_x(gl_WorkGroupSize->clone(shader, NULL))), swizzle_y(gl_WorkGroupSize->clone(shader, NULL))); ir_expression *index_y = mul(swizzle_y(gl_LocalInvocationID), swizzle_x(gl_WorkGroupSize->clone(shader, NULL))); ir_expression *index_y_plus_z = add(index_y, index_z); operand index_x(swizzle_x(gl_LocalInvocationID)); ir_expression *index_x_plus_y_plus_z = add(index_y_plus_z, index_x); ir_instruction *inst = assign(gl_LocalInvocationIndex, index_x_plus_y_plus_z); main_sig->body.push_head(inst); } ir_visitor_status lower_cs_derived_visitor::visit(ir_dereference_variable *ir) { if (ir->var->data.mode == ir_var_system_value && ir->var->data.location == SYSTEM_VALUE_GLOBAL_INVOCATION_ID) { make_gl_GlobalInvocationID(); ir->var = gl_GlobalInvocationID; progress = true; } if (ir->var->data.mode == ir_var_system_value && ir->var->data.location == SYSTEM_VALUE_LOCAL_INVOCATION_INDEX) { make_gl_LocalInvocationIndex(); ir->var = gl_LocalInvocationIndex; progress = true; } return visit_continue; } bool lower_cs_derived(gl_linked_shader *shader) { if (shader->Stage != MESA_SHADER_COMPUTE) return false; lower_cs_derived_visitor v(shader); v.run(shader->ir); return v.progress; }