Home | History | Annotate | Line # | Download | only in d3d12
      1 /*
      2  * Copyright  Microsoft Corporation
      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
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     21  * IN THE SOFTWARE.
     22  */
     23 
     24 #include "nir.h"
     25 #include "nir_builder.h"
     26 #include "d3d12_compiler.h"
     27 #include "d3d12_nir_passes.h"
     28 #include "dxil_nir.h"
     29 #include "program/prog_statevars.h"
     30 
     31 struct lower_state {
     32    nir_variable *uniform; /* (1/w, 1/h, pt_sz, max_sz) */
     33    nir_variable *pos_out;
     34    nir_variable *psiz_out;
     35    nir_variable *point_coord_out[9];
     36    unsigned num_point_coords;
     37    nir_variable *varying_out[VARYING_SLOT_MAX];
     38 
     39    nir_ssa_def *point_dir_imm[4];
     40    nir_ssa_def *point_coord_imm[4];
     41 
     42    /* Current point primitive */
     43    nir_ssa_def *point_pos;
     44    nir_ssa_def *point_size;
     45    nir_ssa_def *varying[VARYING_SLOT_MAX];
     46    unsigned varying_write_mask[VARYING_SLOT_MAX];
     47 
     48    bool sprite_origin_lower_left;
     49    bool point_size_per_vertex;
     50    bool aa_point;
     51 };
     52 
     53 static void
     54 find_outputs(nir_shader *shader, struct lower_state *state)
     55 {
     56    nir_foreach_variable_with_modes(var, shader, nir_var_shader_out) {
     57       switch (var->data.location) {
     58       case VARYING_SLOT_POS:
     59          state->pos_out = var;
     60          break;
     61       case VARYING_SLOT_PSIZ:
     62          state->psiz_out = var;
     63          break;
     64       default:
     65          state->varying_out[var->data.location] = var;
     66          break;
     67       }
     68    }
     69 }
     70 
     71 static nir_ssa_def *
     72 get_point_dir(nir_builder *b, struct lower_state *state, unsigned i)
     73 {
     74    if (state->point_dir_imm[0] == NULL) {
     75       state->point_dir_imm[0] = nir_imm_vec2(b, -1, -1);
     76       state->point_dir_imm[1] = nir_imm_vec2(b, -1, 1);
     77       state->point_dir_imm[2] = nir_imm_vec2(b, 1, -1);
     78       state->point_dir_imm[3] = nir_imm_vec2(b, 1, 1);
     79    }
     80 
     81    return state->point_dir_imm[i];
     82 }
     83 
     84 static nir_ssa_def *
     85 get_point_coord(nir_builder *b, struct lower_state *state, unsigned i)
     86 {
     87    if (state->point_coord_imm[0] == NULL) {
     88       if (state->sprite_origin_lower_left) {
     89          state->point_coord_imm[0] = nir_imm_vec4(b, 0, 0, 0, 1);
     90          state->point_coord_imm[1] = nir_imm_vec4(b, 0, 1, 0, 1);
     91          state->point_coord_imm[2] = nir_imm_vec4(b, 1, 0, 0, 1);
     92          state->point_coord_imm[3] = nir_imm_vec4(b, 1, 1, 0, 1);
     93       } else {
     94          state->point_coord_imm[0] = nir_imm_vec4(b, 0, 1, 0, 1);
     95          state->point_coord_imm[1] = nir_imm_vec4(b, 0, 0, 0, 1);
     96          state->point_coord_imm[2] = nir_imm_vec4(b, 1, 1, 0, 1);
     97          state->point_coord_imm[3] = nir_imm_vec4(b, 1, 0, 0, 1);
     98       }
     99    }
    100 
    101    return state->point_coord_imm[i];
    102 }
    103 
    104 /**
    105  * scaled_point_size = pointSize * pos.w * ViewportSizeRcp
    106  */
    107 static void
    108 get_scaled_point_size(nir_builder *b, struct lower_state *state,
    109                       nir_ssa_def **x, nir_ssa_def **y)
    110 {
    111    /* State uniform contains: (1/ViewportWidth, 1/ViewportHeight, PointSize, MaxPointSize) */
    112    nir_ssa_def *uniform = nir_load_var(b, state->uniform);
    113    nir_ssa_def *point_size = state->point_size;
    114 
    115    /* clamp point-size to valid range */
    116    if (point_size && state->point_size_per_vertex) {
    117       point_size = nir_fmax(b, point_size, nir_imm_float(b, 1.0f));
    118       point_size = nir_fmin(b, point_size, nir_imm_float(b, D3D12_MAX_POINT_SIZE));
    119    } else {
    120       /* Use static point size (from uniform) if the shader output was not set */
    121       point_size = nir_channel(b, uniform, 2);
    122    }
    123 
    124    point_size = nir_fmul(b, point_size, nir_channel(b, state->point_pos, 3));
    125    *x = nir_fmul(b, point_size, nir_channel(b, uniform, 0));
    126    *y = nir_fmul(b, point_size, nir_channel(b, uniform, 1));
    127 }
    128 
    129 static bool
    130 lower_store(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
    131 {
    132    nir_deref_instr *deref = nir_src_as_deref(instr->src[0]);
    133    if (nir_deref_mode_is(deref, nir_var_shader_out)) {
    134       nir_variable *var = nir_deref_instr_get_variable(deref);
    135 
    136       switch (var->data.location) {
    137       case VARYING_SLOT_POS:
    138          state->point_pos = instr->src[1].ssa;
    139          break;
    140       case VARYING_SLOT_PSIZ:
    141          state->point_size = instr->src[1].ssa;
    142          break;
    143       default:
    144          state->varying[var->data.location] = instr->src[1].ssa;
    145          state->varying_write_mask[var->data.location] = nir_intrinsic_write_mask(instr);
    146          break;
    147       }
    148 
    149       nir_instr_remove(&instr->instr);
    150       return true;
    151    }
    152 
    153    return false;
    154 }
    155 
    156 static bool
    157 lower_emit_vertex(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
    158 {
    159    unsigned stream_id = nir_intrinsic_stream_id(instr);
    160 
    161    nir_ssa_def *point_width, *point_height;
    162    get_scaled_point_size(b, state, &point_width, &point_height);
    163 
    164    nir_instr_remove(&instr->instr);
    165 
    166    for (unsigned i = 0; i < 4; i++) {
    167       /* All outputs need to be emitted for each vertex */
    168       for (unsigned slot = 0; slot < VARYING_SLOT_MAX; ++slot) {
    169          if (state->varying[slot] != NULL) {
    170             nir_store_var(b, state->varying_out[slot], state->varying[slot],
    171                           state->varying_write_mask[slot]);
    172          }
    173       }
    174 
    175       /* pos = scaled_point_size * point_dir + point_pos */
    176       nir_ssa_def *point_dir = get_point_dir(b, state, i);
    177       nir_ssa_def *pos = nir_vec4(b,
    178                                   nir_ffma(b,
    179                                            point_width,
    180                                            nir_channel(b, point_dir, 0),
    181                                            nir_channel(b, state->point_pos, 0)),
    182                                   nir_ffma(b,
    183                                            point_height,
    184                                            nir_channel(b, point_dir, 1),
    185                                            nir_channel(b, state->point_pos, 1)),
    186                                   nir_channel(b, state->point_pos, 2),
    187                                   nir_channel(b, state->point_pos, 3));
    188       nir_store_var(b, state->pos_out, pos, 0xf);
    189 
    190       /* point coord */
    191       nir_ssa_def *point_coord = get_point_coord(b, state, i);
    192       for (unsigned j = 0; j < state->num_point_coords; ++j)
    193          nir_store_var(b, state->point_coord_out[j], point_coord, 0xf);
    194 
    195       /* EmitVertex */
    196       nir_emit_vertex(b, .stream_id = stream_id);
    197    }
    198 
    199    /* EndPrimitive */
    200    nir_end_primitive(b, .stream_id = stream_id);
    201 
    202    /* Reset everything */
    203    state->point_pos = NULL;
    204    state->point_size = NULL;
    205    for (unsigned i = 0; i < VARYING_SLOT_MAX; ++i)
    206       state->varying[i] = NULL;
    207 
    208    return true;
    209 }
    210 
    211 static bool
    212 lower_instr(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
    213 {
    214    b->cursor = nir_before_instr(&instr->instr);
    215 
    216    if (instr->intrinsic == nir_intrinsic_store_deref) {
    217       return lower_store(instr, b, state);
    218    } else if (instr->intrinsic == nir_intrinsic_emit_vertex) {
    219       return lower_emit_vertex(instr, b, state);
    220    } else if (instr->intrinsic == nir_intrinsic_end_primitive) {
    221       nir_instr_remove(&instr->instr);
    222       return true;
    223    }
    224 
    225    return false;
    226 }
    227 
    228 bool
    229 d3d12_lower_point_sprite(nir_shader *shader,
    230                          bool sprite_origin_lower_left,
    231                          bool point_size_per_vertex,
    232                          unsigned point_coord_enable,
    233                          uint64_t next_inputs_read)
    234 {
    235    const gl_state_index16 tokens[4] = { STATE_INTERNAL_DRIVER,
    236                                         D3D12_STATE_VAR_PT_SPRITE };
    237    struct lower_state state;
    238    bool progress = false;
    239 
    240    assert(shader->info.gs.output_primitive == GL_POINTS);
    241 
    242    memset(&state, 0, sizeof(state));
    243    find_outputs(shader, &state);
    244    state.sprite_origin_lower_left = sprite_origin_lower_left;
    245    state.point_size_per_vertex = point_size_per_vertex;
    246 
    247    /* Create uniform to retrieve inverse of viewport size and point size:
    248     * (1/ViewportWidth, 1/ViewportHeight, PointSize, MaxPointSize) */
    249    state.uniform = nir_variable_create(shader,
    250                                        nir_var_uniform,
    251                                        glsl_vec4_type(),
    252                                        "d3d12_ViewportSizeRcp");
    253    state.uniform->num_state_slots = 1;
    254    state.uniform->state_slots = ralloc_array(state.uniform, nir_state_slot, 1);
    255    memcpy(state.uniform->state_slots[0].tokens, tokens,
    256           sizeof(state.uniform->state_slots[0].tokens));
    257    shader->num_uniforms++;
    258 
    259    /* Create new outputs for point tex coordinates */
    260    unsigned count = 0;
    261    for (unsigned int sem = 0; sem < 9; sem++) {
    262       if (point_coord_enable & BITFIELD64_BIT(sem)) {
    263          char tmp[100];
    264          unsigned location = VARYING_SLOT_VAR0 + sem;
    265 
    266          snprintf(tmp, ARRAY_SIZE(tmp), "gl_TexCoord%dMESA", count);
    267 
    268          nir_variable *var = nir_variable_create(shader,
    269                                                  nir_var_shader_out,
    270                                                  glsl_vec4_type(),
    271                                                  tmp);
    272          var->data.location = location;
    273          state.point_coord_out[count++] = var;
    274       }
    275    }
    276    state.num_point_coords = count;
    277    if (point_coord_enable) {
    278       dxil_reassign_driver_locations(shader, nir_var_shader_out,
    279                                       next_inputs_read);
    280    }
    281 
    282    nir_foreach_function(function, shader) {
    283       if (function->impl) {
    284          nir_builder builder;
    285          nir_builder_init(&builder, function->impl);
    286          nir_foreach_block(block, function->impl) {
    287             nir_foreach_instr_safe(instr, block) {
    288                if (instr->type == nir_instr_type_intrinsic)
    289                   progress |= lower_instr(nir_instr_as_intrinsic(instr),
    290                                           &builder,
    291                                           &state);
    292             }
    293          }
    294 
    295          nir_metadata_preserve(function->impl, nir_metadata_block_index |
    296                                                nir_metadata_dominance);
    297       }
    298    }
    299 
    300    shader->info.gs.output_primitive = GL_TRIANGLE_STRIP;
    301    shader->info.gs.vertices_out *= 4;
    302 
    303    return progress;
    304 }
    305