Home | History | Annotate | Download | only in state_tracker
      1 /*
      2  * Copyright  2016 Red Hat
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  */
     23 
     24 /* Lowering pass that lowers accesses to built-in uniform variables.
     25  * Built-in uniforms are not necessarily packed the same way that
     26  * normal uniform structs are, for example:
     27  *
     28  *    struct gl_FogParameters {
     29  *       vec4 color;
     30  *       float density;
     31  *       float start;
     32  *       float end;
     33  *       float scale;
     34  *    };
     35  *
     36  * is packed into vec4[2], whereas the same struct would be packed
     37  * (by gallium), as vec4[5] if it where not built-in.  Because of
     38  * this, we need to replace (for example) access like:
     39  *
     40  *    vec1 ssa_1 = intrinsic load_var () (gl_Fog.start) ()
     41  *
     42  * with:
     43  *
     44  *    vec4 ssa_2 = intrinsic load_var () (fog.params) ()
     45  *    vec1 ssa_1 = ssa_2.y
     46  *
     47  * with appropriate substitutions in the uniform variables list:
     48  *
     49  *    decl_var uniform INTERP_MODE_NONE gl_FogParameters gl_Fog (0, 0)
     50  *
     51  * would become:
     52  *
     53  *    decl_var uniform INTERP_MODE_NONE vec4 state.fog.color (0, 0)
     54  *    decl_var uniform INTERP_MODE_NONE vec4 state.fog.params (0, 1)
     55  *
     56  * See in particular 'struct gl_builtin_uniform_element'.
     57  */
     58 
     59 #include "compiler/nir/nir.h"
     60 #include "compiler/nir/nir_builder.h"
     61 #include "st_nir.h"
     62 #include "compiler/glsl/ir.h"
     63 #include "uniforms.h"
     64 #include "program/prog_instruction.h"
     65 
     66 typedef struct {
     67    nir_shader *shader;
     68    nir_builder builder;
     69    void *mem_ctx;
     70 } lower_builtin_state;
     71 
     72 static const struct gl_builtin_uniform_element *
     73 get_element(const struct gl_builtin_uniform_desc *desc, nir_deref_var *deref)
     74 {
     75    nir_deref *tail = &deref->deref;
     76 
     77    if ((desc->num_elements == 1) && (desc->elements[0].field == NULL))
     78       return NULL;
     79 
     80    /* we handle arrays in get_variable(): */
     81    if (tail->child->deref_type == nir_deref_type_array)
     82       tail = tail->child;
     83 
     84    /* don't need to deal w/ non-struct or array of non-struct: */
     85    if (!tail->child)
     86       return NULL;
     87 
     88    if (tail->child->deref_type != nir_deref_type_struct)
     89       return NULL;
     90 
     91    nir_deref_struct *deref_struct = nir_deref_as_struct(tail->child);
     92 
     93    assert(deref_struct->index < desc->num_elements);
     94 
     95    return &desc->elements[deref_struct->index];
     96 }
     97 
     98 static nir_variable *
     99 get_variable(lower_builtin_state *state, nir_deref_var *deref,
    100              const struct gl_builtin_uniform_element *element)
    101 {
    102    nir_shader *shader = state->shader;
    103    int tokens[STATE_LENGTH];
    104 
    105    memcpy(tokens, element->tokens, sizeof(tokens));
    106 
    107    if (deref->deref.child->deref_type == nir_deref_type_array) {
    108       nir_deref_array *darr = nir_deref_as_array(deref->deref.child);
    109 
    110       assert(darr->deref_array_type == nir_deref_array_type_direct);
    111 
    112       /* we need to fixup the array index slot: */
    113       switch (tokens[0]) {
    114       case STATE_MODELVIEW_MATRIX:
    115       case STATE_PROJECTION_MATRIX:
    116       case STATE_MVP_MATRIX:
    117       case STATE_TEXTURE_MATRIX:
    118       case STATE_PROGRAM_MATRIX:
    119       case STATE_LIGHT:
    120       case STATE_LIGHTPROD:
    121       case STATE_TEXGEN:
    122       case STATE_TEXENV_COLOR:
    123       case STATE_CLIPPLANE:
    124          tokens[1] = darr->base_offset;
    125          break;
    126       }
    127    }
    128 
    129    char *name = _mesa_program_state_string((gl_state_index *)tokens);
    130 
    131    nir_foreach_variable(var, &shader->uniforms) {
    132       if (strcmp(var->name, name) == 0) {
    133          free(name);
    134          return var;
    135       }
    136    }
    137 
    138    /* variable doesn't exist yet, so create it: */
    139    nir_variable *var =
    140       nir_variable_create(shader, nir_var_uniform, glsl_vec4_type(), name);
    141 
    142    var->num_state_slots = 1;
    143    var->state_slots = ralloc_array(var, nir_state_slot, 1);
    144    memcpy(var->state_slots[0].tokens, tokens,
    145           sizeof(var->state_slots[0].tokens));
    146 
    147    free(name);
    148 
    149    return var;
    150 }
    151 
    152 static bool
    153 lower_builtin_block(lower_builtin_state *state, nir_block *block)
    154 {
    155    nir_builder *b = &state->builder;
    156 
    157    nir_foreach_instr_safe(instr, block) {
    158       if (instr->type != nir_instr_type_intrinsic)
    159          continue;
    160 
    161       nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
    162 
    163       if (intrin->intrinsic != nir_intrinsic_load_var)
    164          continue;
    165 
    166       nir_variable *var = intrin->variables[0]->var;
    167       if (var->data.mode != nir_var_uniform)
    168          continue;
    169 
    170       /* built-in's will always start with "gl_" */
    171       if (strncmp(var->name, "gl_", 3) != 0)
    172          continue;
    173 
    174       const struct gl_builtin_uniform_desc *desc =
    175          _mesa_glsl_get_builtin_uniform_desc(var->name);
    176 
    177       /* if no descriptor, it isn't something we need to handle specially: */
    178       if (!desc)
    179          continue;
    180 
    181       const struct gl_builtin_uniform_element *element =
    182          get_element(desc, intrin->variables[0]);
    183 
    184       /* matrix elements (array_deref) do not need special handling: */
    185       if (!element)
    186          continue;
    187 
    188       /* remove existing var from uniform list: */
    189       exec_node_remove(&var->node);
    190       /* the _self_link() ensures we can remove multiple times, rather than
    191        * trying to keep track of what we have already removed:
    192        */
    193       exec_node_self_link(&var->node);
    194 
    195       nir_variable *new_var =
    196          get_variable(state, intrin->variables[0], element);
    197 
    198       b->cursor = nir_before_instr(instr);
    199 
    200       nir_ssa_def *def = nir_load_var(b, new_var);
    201 
    202       /* swizzle the result: */
    203       unsigned swiz[4];
    204       for (unsigned i = 0; i < 4; i++) {
    205          swiz[i] = GET_SWZ(element->swizzle, i);
    206          assert(swiz[i] <= SWIZZLE_W);
    207       }
    208       def = nir_swizzle(b, def, swiz, intrin->num_components, true);
    209 
    210       /* and rewrite uses of original instruction: */
    211       assert(intrin->dest.is_ssa);
    212       nir_ssa_def_rewrite_uses(&intrin->dest.ssa, nir_src_for_ssa(def));
    213 
    214       /* at this point intrin should be unused.  We need to remove it
    215        * (rather than waiting for DCE pass) to avoid dangling reference
    216        * to remove'd var.  And we have to remove the original uniform
    217        * var since we don't want it to get uniform space allocated.
    218        */
    219       exec_node_remove(&intrin->instr.node);
    220    }
    221 
    222    return true;
    223 }
    224 
    225 static void
    226 lower_builtin_impl(lower_builtin_state *state, nir_function_impl *impl)
    227 {
    228    nir_builder_init(&state->builder, impl);
    229    state->mem_ctx = ralloc_parent(impl);
    230 
    231    nir_foreach_block(block, impl) {
    232       lower_builtin_block(state, block);
    233    }
    234 
    235    nir_metadata_preserve(impl, nir_metadata_block_index |
    236                                nir_metadata_dominance);
    237 }
    238 
    239 void
    240 st_nir_lower_builtin(nir_shader *shader)
    241 {
    242    lower_builtin_state state;
    243    state.shader = shader;
    244    nir_foreach_function(function, shader) {
    245       if (function->impl)
    246          lower_builtin_impl(&state, function->impl);
    247    }
    248 }
    249