| /* |
| * Copyright © Microsoft Corporation |
| * |
| * 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. |
| */ |
| |
| #include "dxil_signature.h" |
| #include "dxil_enums.h" |
| #include "dxil_module.h" |
| |
| #include "glsl_types.h" |
| #include "nir_to_dxil.h" |
| #include "util/u_debug.h" |
| |
| #include <string.h> |
| |
| |
| struct semantic_info { |
| enum dxil_semantic_kind kind; |
| char name[64]; |
| int index; |
| enum dxil_prog_sig_comp_type comp_type; |
| uint8_t sig_comp_type; |
| int32_t start_row; |
| int32_t rows; |
| uint8_t start_col; |
| uint8_t cols; |
| uint8_t interpolation; |
| uint8_t stream; |
| const char *sysvalue_name; |
| }; |
| |
| |
| static bool |
| is_depth_output(enum dxil_semantic_kind kind) |
| { |
| return kind == DXIL_SEM_DEPTH || kind == DXIL_SEM_DEPTH_GE || |
| kind == DXIL_SEM_DEPTH_LE || kind == DXIL_SEM_STENCIL_REF; |
| } |
| |
| static uint8_t |
| get_interpolation(nir_variable *var) |
| { |
| if (var->data.patch) |
| return DXIL_INTERP_UNDEFINED; |
| |
| if (glsl_type_is_integer(glsl_without_array_or_matrix(var->type))) |
| return DXIL_INTERP_CONSTANT; |
| |
| if (var->data.sample) { |
| if (var->data.location == VARYING_SLOT_POS) |
| return DXIL_INTERP_LINEAR_NOPERSPECTIVE_SAMPLE; |
| switch (var->data.interpolation) { |
| case INTERP_MODE_NONE: return DXIL_INTERP_LINEAR_SAMPLE; |
| case INTERP_MODE_FLAT: return DXIL_INTERP_CONSTANT; |
| case INTERP_MODE_NOPERSPECTIVE: return DXIL_INTERP_LINEAR_NOPERSPECTIVE_SAMPLE; |
| case INTERP_MODE_SMOOTH: return DXIL_INTERP_LINEAR_SAMPLE; |
| } |
| } else if (unlikely(var->data.centroid)) { |
| if (var->data.location == VARYING_SLOT_POS) |
| return DXIL_INTERP_LINEAR_NOPERSPECTIVE_CENTROID; |
| switch (var->data.interpolation) { |
| case INTERP_MODE_NONE: return DXIL_INTERP_LINEAR_CENTROID; |
| case INTERP_MODE_FLAT: return DXIL_INTERP_CONSTANT; |
| case INTERP_MODE_NOPERSPECTIVE: return DXIL_INTERP_LINEAR_NOPERSPECTIVE_CENTROID; |
| case INTERP_MODE_SMOOTH: return DXIL_INTERP_LINEAR_CENTROID; |
| } |
| } else { |
| if (var->data.location == VARYING_SLOT_POS) |
| return DXIL_INTERP_LINEAR_NOPERSPECTIVE; |
| switch (var->data.interpolation) { |
| case INTERP_MODE_NONE: return DXIL_INTERP_LINEAR; |
| case INTERP_MODE_FLAT: return DXIL_INTERP_CONSTANT; |
| case INTERP_MODE_NOPERSPECTIVE: return DXIL_INTERP_LINEAR_NOPERSPECTIVE; |
| case INTERP_MODE_SMOOTH: return DXIL_INTERP_LINEAR; |
| } |
| } |
| |
| return DXIL_INTERP_LINEAR; |
| } |
| |
| static const char * |
| in_sysvalue_name(nir_variable *var) |
| { |
| switch (var->data.location) { |
| case VARYING_SLOT_POS: |
| return "POS"; |
| case VARYING_SLOT_FACE: |
| return "FACE"; |
| case VARYING_SLOT_LAYER: |
| return "RTINDEX"; |
| default: |
| return "NONE"; |
| } |
| } |
| |
| /* |
| * The signatures are written into the stream in two pieces: |
| * DxilProgramSignatureElement is a fixes size structure that gets dumped |
| * to the stream in order of the registers and each contains an offset |
| * to the semantic name string. Then these strings are dumped into the stream. |
| */ |
| static unsigned |
| get_additional_semantic_info(nir_shader *s, nir_variable *var, struct semantic_info *info, |
| unsigned next_row, unsigned clip_size) |
| { |
| const struct glsl_type *type = var->type; |
| if (nir_is_arrayed_io(var, s->info.stage)) |
| type = glsl_get_array_element(type); |
| |
| info->comp_type = |
| dxil_get_prog_sig_comp_type(type); |
| |
| bool is_depth = is_depth_output(info->kind); |
| |
| if (!glsl_type_is_struct(type)) { |
| info->sig_comp_type = dxil_get_comp_type(type); |
| } else if (var->data.interpolation == INTERP_MODE_FLAT) { |
| info->sig_comp_type = DXIL_COMP_TYPE_U32; |
| info->comp_type = DXIL_PROG_SIG_COMP_TYPE_UINT32; |
| } else { |
| info->sig_comp_type = DXIL_COMP_TYPE_F32; |
| info->comp_type = DXIL_PROG_SIG_COMP_TYPE_FLOAT32; |
| } |
| |
| bool is_gs_input = s->info.stage == MESA_SHADER_GEOMETRY && |
| (var->data.mode & (nir_var_shader_in | nir_var_system_value)); |
| |
| info->stream = var->data.stream; |
| info->rows = 1; |
| if (info->kind == DXIL_SEM_TARGET) { |
| info->start_row = info->index; |
| info->cols = (uint8_t)glsl_get_components(type); |
| } else if (is_depth || |
| (info->kind == DXIL_SEM_PRIMITIVE_ID && is_gs_input) || |
| info->kind == DXIL_SEM_COVERAGE || |
| info->kind == DXIL_SEM_SAMPLE_INDEX) { |
| // This turns into a 'N/A' mask in the disassembly |
| info->start_row = -1; |
| info->cols = 1; |
| } else if (info->kind == DXIL_SEM_TESS_FACTOR || |
| info->kind == DXIL_SEM_INSIDE_TESS_FACTOR) { |
| assert(var->data.compact); |
| info->start_row = next_row; |
| info->rows = glsl_get_aoa_size(type); |
| info->cols = 1; |
| next_row += info->rows; |
| } else if (var->data.compact) { |
| if (var->data.location_frac) { |
| info->start_row = next_row - 1; |
| } else { |
| info->start_row = next_row; |
| next_row++; |
| } |
| |
| assert(glsl_type_is_array(type) && info->kind == DXIL_SEM_CLIP_DISTANCE); |
| unsigned num_floats = glsl_get_aoa_size(type); |
| unsigned start_offset = (var->data.location - VARYING_SLOT_CLIP_DIST0) * 4 + |
| var->data.location_frac; |
| |
| if (start_offset >= clip_size) { |
| info->kind = DXIL_SEM_CULL_DISTANCE; |
| snprintf(info->name, 64, "SV_CullDistance"); |
| } |
| info->cols = num_floats; |
| info->start_col = (uint8_t)var->data.location_frac; |
| } else { |
| info->start_row = next_row; |
| info->rows = glsl_count_vec4_slots(type, false, false); |
| if (glsl_type_is_array(type)) |
| type = glsl_get_array_element(type); |
| next_row += info->rows; |
| info->start_col = (uint8_t)var->data.location_frac; |
| info->cols = MIN2(glsl_get_component_slots(type), 4); |
| } |
| |
| return next_row; |
| } |
| |
| typedef void (*semantic_info_proc)(nir_variable *var, struct semantic_info *info, gl_shader_stage stage); |
| |
| static void |
| get_semantic_vs_in_name(nir_variable *var, struct semantic_info *info, gl_shader_stage stage) |
| { |
| strcpy(info->name, "TEXCOORD"); |
| info->index = var->data.driver_location; |
| info->kind = DXIL_SEM_ARBITRARY; |
| } |
| |
| static void |
| get_semantic_sv_name(nir_variable *var, struct semantic_info *info, gl_shader_stage stage) |
| { |
| if (stage != MESA_SHADER_VERTEX) |
| info->interpolation = get_interpolation(var); |
| |
| switch (var->data.location) { |
| case SYSTEM_VALUE_VERTEX_ID_ZERO_BASE: |
| info->kind = DXIL_SEM_VERTEX_ID; |
| break; |
| case SYSTEM_VALUE_FRONT_FACE: |
| info->kind = DXIL_SEM_IS_FRONT_FACE; |
| break; |
| case SYSTEM_VALUE_INSTANCE_ID: |
| info->kind = DXIL_SEM_INSTANCE_ID; |
| break; |
| case SYSTEM_VALUE_PRIMITIVE_ID: |
| info->kind = DXIL_SEM_PRIMITIVE_ID; |
| break; |
| case SYSTEM_VALUE_SAMPLE_ID: |
| info->kind = DXIL_SEM_SAMPLE_INDEX; |
| break; |
| default: |
| unreachable("unsupported system value"); |
| } |
| strncpy(info->name, var->name, ARRAY_SIZE(info->name) - 1); |
| } |
| |
| static void |
| get_semantic_ps_outname(nir_variable *var, struct semantic_info *info) |
| { |
| info->kind = DXIL_SEM_INVALID; |
| switch (var->data.location) { |
| case FRAG_RESULT_COLOR: |
| snprintf(info->name, 64, "%s", "SV_Target"); |
| info->index = var->data.index; |
| info->kind = DXIL_SEM_TARGET; |
| break; |
| case FRAG_RESULT_DATA0: |
| case FRAG_RESULT_DATA1: |
| case FRAG_RESULT_DATA2: |
| case FRAG_RESULT_DATA3: |
| case FRAG_RESULT_DATA4: |
| case FRAG_RESULT_DATA5: |
| case FRAG_RESULT_DATA6: |
| case FRAG_RESULT_DATA7: |
| snprintf(info->name, 64, "%s", "SV_Target"); |
| info->index = var->data.location - FRAG_RESULT_DATA0; |
| if (var->data.location == FRAG_RESULT_DATA0 && |
| var->data.index > 0) |
| info->index = var->data.index; |
| info->kind = DXIL_SEM_TARGET; |
| break; |
| case FRAG_RESULT_DEPTH: |
| snprintf(info->name, 64, "%s", "SV_Depth"); |
| info->kind = DXIL_SEM_DEPTH; |
| break; |
| case FRAG_RESULT_STENCIL: |
| snprintf(info->name, 64, "%s", "SV_StencilRef"); |
| info->kind = DXIL_SEM_STENCIL_REF; //?? |
| break; |
| case FRAG_RESULT_SAMPLE_MASK: |
| snprintf(info->name, 64, "%s", "SV_Coverage"); |
| info->kind = DXIL_SEM_COVERAGE; //?? |
| break; |
| default: |
| snprintf(info->name, 64, "%s", "UNDEFINED"); |
| break; |
| } |
| } |
| |
| static void |
| get_semantic_name(nir_variable *var, struct semantic_info *info, |
| const struct glsl_type *type) |
| { |
| info->kind = DXIL_SEM_INVALID; |
| info->interpolation = get_interpolation(var); |
| switch (var->data.location) { |
| |
| case VARYING_SLOT_POS: |
| assert(glsl_get_components(type) == 4); |
| snprintf(info->name, 64, "%s", "SV_Position"); |
| info->kind = DXIL_SEM_POSITION; |
| break; |
| |
| case VARYING_SLOT_FACE: |
| assert(glsl_get_components(var->type) == 1); |
| snprintf(info->name, 64, "%s", "SV_IsFrontFace"); |
| info->kind = DXIL_SEM_IS_FRONT_FACE; |
| break; |
| |
| case VARYING_SLOT_PRIMITIVE_ID: |
| assert(glsl_get_components(var->type) == 1); |
| snprintf(info->name, 64, "%s", "SV_PrimitiveID"); |
| info->kind = DXIL_SEM_PRIMITIVE_ID; |
| break; |
| |
| case VARYING_SLOT_CLIP_DIST1: |
| info->index = 1; |
| FALLTHROUGH; |
| case VARYING_SLOT_CLIP_DIST0: |
| assert(var->data.location == VARYING_SLOT_CLIP_DIST1 || info->index == 0); |
| snprintf(info->name, 64, "%s", "SV_ClipDistance"); |
| info->kind = DXIL_SEM_CLIP_DISTANCE; |
| break; |
| |
| case VARYING_SLOT_TESS_LEVEL_INNER: |
| assert(glsl_get_components(var->type) <= 2); |
| snprintf(info->name, 64, "%s", "SV_InsideTessFactor"); |
| info->kind = DXIL_SEM_INSIDE_TESS_FACTOR; |
| break; |
| |
| case VARYING_SLOT_TESS_LEVEL_OUTER: |
| assert(glsl_get_components(var->type) <= 4); |
| snprintf(info->name, 64, "%s", "SV_TessFactor"); |
| info->kind = DXIL_SEM_TESS_FACTOR; |
| break; |
| |
| case VARYING_SLOT_VIEWPORT: |
| assert(glsl_get_components(var->type) == 1); |
| snprintf(info->name, 64, "%s", "SV_ViewportArrayIndex"); |
| info->kind = DXIL_SEM_VIEWPORT_ARRAY_INDEX; |
| break; |
| |
| case VARYING_SLOT_LAYER: |
| assert(glsl_get_components(var->type) == 1); |
| snprintf(info->name, 64, "%s", "SV_RenderTargetArrayIndex"); |
| info->kind = DXIL_SEM_RENDERTARGET_ARRAY_INDEX; |
| break; |
| |
| default: { |
| info->index = var->data.driver_location; |
| strcpy(info->name, "TEXCOORD"); |
| info->kind = DXIL_SEM_ARBITRARY; |
| } |
| } |
| } |
| |
| static void |
| get_semantic_in_name(nir_variable *var, struct semantic_info *info, gl_shader_stage stage) |
| { |
| const struct glsl_type *type = var->type; |
| if (nir_is_arrayed_io(var, stage) && |
| glsl_type_is_array(type)) |
| type = glsl_get_array_element(type); |
| |
| get_semantic_name(var, info, type); |
| info->sysvalue_name = in_sysvalue_name(var); |
| } |
| |
| |
| static enum dxil_prog_sig_semantic |
| prog_semantic_from_kind(enum dxil_semantic_kind kind, unsigned num_vals, unsigned start_val) |
| { |
| switch (kind) { |
| case DXIL_SEM_ARBITRARY: return DXIL_PROG_SEM_UNDEFINED; |
| case DXIL_SEM_VERTEX_ID: return DXIL_PROG_SEM_VERTEX_ID; |
| case DXIL_SEM_INSTANCE_ID: return DXIL_PROG_SEM_INSTANCE_ID; |
| case DXIL_SEM_POSITION: return DXIL_PROG_SEM_POSITION; |
| case DXIL_SEM_COVERAGE: return DXIL_PROG_SEM_COVERAGE; |
| case DXIL_SEM_INNER_COVERAGE: return DXIL_PROG_SEM_INNER_COVERAGE; |
| case DXIL_SEM_PRIMITIVE_ID: return DXIL_PROG_SEM_PRIMITIVE_ID; |
| case DXIL_SEM_SAMPLE_INDEX: return DXIL_PROG_SEM_SAMPLE_INDEX; |
| case DXIL_SEM_IS_FRONT_FACE: return DXIL_PROG_SEM_IS_FRONTFACE; |
| case DXIL_SEM_RENDERTARGET_ARRAY_INDEX: return DXIL_PROG_SEM_RENDERTARGET_ARRAY_INDEX; |
| case DXIL_SEM_VIEWPORT_ARRAY_INDEX: return DXIL_PROG_SEM_VIEWPORT_ARRAY_INDEX; |
| case DXIL_SEM_CLIP_DISTANCE: return DXIL_PROG_SEM_CLIP_DISTANCE; |
| case DXIL_SEM_CULL_DISTANCE: return DXIL_PROG_SEM_CULL_DISTANCE; |
| case DXIL_SEM_BARYCENTRICS: return DXIL_PROG_SEM_BARYCENTRICS; |
| case DXIL_SEM_SHADING_RATE: return DXIL_PROG_SEM_SHADING_RATE; |
| case DXIL_SEM_CULL_PRIMITIVE: return DXIL_PROG_SEM_CULL_PRIMITIVE; |
| case DXIL_SEM_TARGET: return DXIL_PROG_SEM_TARGET; |
| case DXIL_SEM_DEPTH: return DXIL_PROG_SEM_DEPTH; |
| case DXIL_SEM_DEPTH_LE: return DXIL_PROG_SEM_DEPTH_LE; |
| case DXIL_SEM_DEPTH_GE: return DXIL_PROG_SEM_DEPTH_GE; |
| case DXIL_SEM_STENCIL_REF: return DXIL_PROG_SEM_STENCIL_REF; |
| case DXIL_SEM_TESS_FACTOR: |
| switch (num_vals) { |
| case 4: return DXIL_PROG_SEM_FINAL_QUAD_EDGE_TESSFACTOR; |
| case 3: return DXIL_PROG_SEM_FINAL_TRI_EDGE_TESSFACTOR; |
| case 2: return start_val == 0 ? |
| DXIL_PROG_SEM_FINAL_LINE_DENSITY_TESSFACTOR : |
| DXIL_PROG_SEM_FINAL_LINE_DETAIL_TESSFACTOR; |
| default: |
| unreachable("Invalid row count for tess factor"); |
| } |
| case DXIL_SEM_INSIDE_TESS_FACTOR: |
| switch (num_vals) { |
| case 2: return DXIL_PROG_SEM_FINAL_QUAD_INSIDE_EDGE_TESSFACTOR; |
| case 1: return DXIL_PROG_SEM_FINAL_TRI_INSIDE_EDGE_TESSFACTOR; |
| default: |
| unreachable("Invalid row count for inner tess factor"); |
| } |
| default: |
| return DXIL_PROG_SEM_UNDEFINED; |
| } |
| } |
| |
| static |
| uint32_t |
| copy_semantic_name_to_string(struct _mesa_string_buffer *string_out, const char *name) |
| { |
| /* copy the semantic name */ |
| uint32_t retval = string_out->length; |
| size_t name_len = strlen(name) + 1; |
| _mesa_string_buffer_append_len(string_out, name, name_len); |
| return retval; |
| } |
| |
| static |
| uint32_t |
| append_semantic_index_to_table(struct dxil_psv_sem_index_table *table, uint32_t index, |
| uint32_t num_rows) |
| { |
| for (unsigned i = 0; i < table->size; ++i) { |
| unsigned j = 0; |
| for (; j < num_rows && i + j < table->size; ++j) |
| if (table->data[i + j] != index + j) |
| break; |
| if (j == num_rows) |
| return i; |
| else if (j > 0) |
| i += j - 1; |
| } |
| uint32_t retval = table->size; |
| assert(table->size + num_rows <= 80); |
| for (unsigned i = 0; i < num_rows; ++i) |
| table->data[table->size++] = index + i; |
| return retval; |
| } |
| |
| static const struct dxil_mdnode * |
| fill_SV_param_nodes(struct dxil_module *mod, unsigned record_id, |
| const struct dxil_signature_record *rec, |
| const struct dxil_psv_signature_element *psv, |
| bool is_input) { |
| |
| const struct dxil_mdnode *SV_params_nodes[11]; |
| /* For this to always work we should use vectorize_io, but for FS out and VS in |
| * this is not implemented globally */ |
| const struct dxil_mdnode *flattened_semantics[256]; |
| |
| for (unsigned i = 0; i < rec->num_elements; ++i) |
| flattened_semantics[i] = dxil_get_metadata_int32(mod, rec->elements[i].semantic_index); |
| |
| SV_params_nodes[0] = dxil_get_metadata_int32(mod, (int)record_id); // Unique element ID |
| SV_params_nodes[1] = dxil_get_metadata_string(mod, rec->name); // Element name |
| SV_params_nodes[2] = dxil_get_metadata_int8(mod, rec->sig_comp_type); // Element type |
| SV_params_nodes[3] = dxil_get_metadata_int8(mod, (int8_t)psv->semantic_kind); // Effective system value |
| SV_params_nodes[4] = dxil_get_metadata_node(mod, flattened_semantics, |
| rec->num_elements); // Semantic index vector |
| SV_params_nodes[5] = dxil_get_metadata_int8(mod, psv->interpolation_mode); // Interpolation mode |
| SV_params_nodes[6] = dxil_get_metadata_int32(mod, psv->rows); // Number of rows |
| SV_params_nodes[7] = dxil_get_metadata_int8(mod, psv->cols_and_start & 0xf); // Number of columns |
| SV_params_nodes[8] = dxil_get_metadata_int32(mod, rec->elements[0].reg); // Element packing start row |
| SV_params_nodes[9] = dxil_get_metadata_int8(mod, (psv->cols_and_start >> 4) & 0x3); // Element packing start column |
| |
| const struct dxil_mdnode *SV_metadata[6]; |
| unsigned num_metadata_nodes = 0; |
| if (rec->elements[0].stream != 0) { |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int32(mod, DXIL_SIGNATURE_ELEMENT_OUTPUT_STREAM); |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int32(mod, rec->elements[0].stream); |
| } |
| uint8_t usage_mask = rec->elements[0].always_reads_mask; |
| if (!is_input) |
| usage_mask = 0xf & ~rec->elements[0].never_writes_mask; |
| if (usage_mask && mod->minor_validator >= 5) { |
| usage_mask >>= (psv->cols_and_start >> 4) & 0x3; |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int32(mod, DXIL_SIGNATURE_ELEMENT_USAGE_COMPONENT_MASK); |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int8(mod, usage_mask); |
| } |
| |
| uint8_t dynamic_index_mask = psv->dynamic_mask_and_stream & 0xf; |
| if (dynamic_index_mask) { |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int32(mod, DXIL_SIGNATURE_ELEMENT_DYNAMIC_INDEX_COMPONENT_MASK); |
| SV_metadata[num_metadata_nodes++] = dxil_get_metadata_int8(mod, dynamic_index_mask); |
| } |
| |
| SV_params_nodes[10] = num_metadata_nodes ? dxil_get_metadata_node(mod, SV_metadata, num_metadata_nodes) : NULL; |
| |
| return dxil_get_metadata_node(mod, SV_params_nodes, ARRAY_SIZE(SV_params_nodes)); |
| } |
| |
| static void |
| fill_signature_element(struct dxil_signature_element *elm, |
| struct semantic_info *semantic, |
| unsigned row) |
| { |
| memset(elm, 0, sizeof(struct dxil_signature_element)); |
| elm->stream = semantic->stream; |
| // elm->semantic_name_offset = 0; // Offset needs to be filled out when writing |
| elm->semantic_index = semantic->index + row; |
| elm->system_value = (uint32_t) prog_semantic_from_kind(semantic->kind, semantic->rows, row); |
| elm->comp_type = (uint32_t) semantic->comp_type; |
| elm->reg = semantic->start_row + row; |
| |
| assert(semantic->cols + semantic->start_col <= 4); |
| elm->mask = (uint8_t) (((1 << semantic->cols) - 1) << semantic->start_col); |
| elm->min_precision = DXIL_MIN_PREC_DEFAULT; |
| } |
| |
| static bool |
| fill_psv_signature_element(struct dxil_psv_signature_element *psv_elm, |
| struct semantic_info *semantic, struct dxil_module *mod) |
| { |
| memset(psv_elm, 0, sizeof(struct dxil_psv_signature_element)); |
| psv_elm->rows = semantic->rows; |
| if (semantic->start_row >= 0) { |
| assert(semantic->start_row < 256); |
| psv_elm->start_row = semantic->start_row; |
| psv_elm->cols_and_start = (1u << 6) | (semantic->start_col << 4) | semantic->cols; |
| } else { |
| /* The validation expects that the the start row is not egative |
| * and apparently the extra bit in the cols_and_start indicates that the |
| * row is meant literally, so don't set it in this case. |
| * (Source of information: Comparing with the validation structures |
| * created by dxcompiler) |
| */ |
| psv_elm->start_row = 0; |
| psv_elm->cols_and_start = (semantic->start_col << 4) | semantic->cols; |
| } |
| psv_elm->semantic_kind = (uint8_t)semantic->kind; |
| psv_elm->component_type = semantic->comp_type; |
| psv_elm->interpolation_mode = semantic->interpolation; |
| psv_elm->dynamic_mask_and_stream = (semantic->stream) << 4; |
| if (semantic->kind == DXIL_SEM_ARBITRARY && strlen(semantic->name)) { |
| psv_elm->semantic_name_offset = |
| copy_semantic_name_to_string(mod->sem_string_table, semantic->name); |
| |
| /* TODO: clean up memory */ |
| if (psv_elm->semantic_name_offset == (uint32_t)-1) |
| return false; |
| } |
| |
| psv_elm->semantic_indexes_offset = |
| append_semantic_index_to_table(&mod->sem_index_table, semantic->index, semantic->rows); |
| |
| return true; |
| } |
| |
| static bool |
| fill_io_signature(struct dxil_module *mod, int id, |
| struct semantic_info *semantic, |
| struct dxil_signature_record *rec, |
| struct dxil_psv_signature_element *psv_elm) |
| { |
| rec->name = ralloc_strdup(mod->ralloc_ctx, semantic->name); |
| rec->num_elements = semantic->rows; |
| rec->sig_comp_type = semantic->sig_comp_type; |
| |
| for (unsigned i = 0; i < semantic->rows; ++i) |
| fill_signature_element(&rec->elements[i], semantic, i); |
| return fill_psv_signature_element(psv_elm, semantic, mod); |
| } |
| |
| static unsigned |
| get_input_signature_group(struct dxil_module *mod, |
| unsigned num_inputs, |
| nir_shader *s, nir_variable_mode modes, |
| semantic_info_proc get_semantics, unsigned *row_iter, |
| unsigned input_clip_size) |
| { |
| nir_foreach_variable_with_modes(var, s, modes) { |
| if (var->data.patch) |
| continue; |
| |
| struct semantic_info semantic = {0}; |
| get_semantics(var, &semantic, s->info.stage); |
| mod->inputs[num_inputs].sysvalue = semantic.sysvalue_name; |
| *row_iter = get_additional_semantic_info(s, var, &semantic, *row_iter, input_clip_size); |
| |
| mod->input_mappings[var->data.driver_location] = num_inputs; |
| struct dxil_psv_signature_element *psv_elm = &mod->psv_inputs[num_inputs]; |
| |
| if (!fill_io_signature(mod, num_inputs, &semantic, |
| &mod->inputs[num_inputs], psv_elm)) |
| return 0; |
| |
| mod->num_psv_inputs = MAX2(mod->num_psv_inputs, |
| semantic.start_row + semantic.rows); |
| |
| ++num_inputs; |
| assert(num_inputs < VARYING_SLOT_MAX); |
| } |
| return num_inputs; |
| } |
| |
| static void |
| process_input_signature(struct dxil_module *mod, nir_shader *s, unsigned input_clip_size) |
| { |
| if (s->info.stage == MESA_SHADER_KERNEL) |
| return; |
| unsigned next_row = 0; |
| |
| mod->num_sig_inputs = get_input_signature_group(mod, 0, |
| s, nir_var_shader_in, |
| s->info.stage == MESA_SHADER_VERTEX ? |
| get_semantic_vs_in_name : get_semantic_in_name, |
| &next_row, input_clip_size); |
| |
| mod->num_sig_inputs = get_input_signature_group(mod, mod->num_sig_inputs, |
| s, nir_var_system_value, |
| get_semantic_sv_name, |
| &next_row, input_clip_size); |
| |
| } |
| |
| static const char *out_sysvalue_name(nir_variable *var) |
| { |
| switch (var->data.location) { |
| case VARYING_SLOT_FACE: |
| return "FACE"; |
| case VARYING_SLOT_POS: |
| return "POS"; |
| case VARYING_SLOT_CLIP_DIST0: |
| case VARYING_SLOT_CLIP_DIST1: |
| return "CLIPDST"; |
| case VARYING_SLOT_PRIMITIVE_ID: |
| return "PRIMID"; |
| default: |
| return "NO"; |
| } |
| } |
| |
| static void |
| process_output_signature(struct dxil_module *mod, nir_shader *s) |
| { |
| unsigned num_outputs = 0; |
| unsigned next_row = 0; |
| nir_foreach_variable_with_modes(var, s, nir_var_shader_out) { |
| struct semantic_info semantic = {0}; |
| if (var->data.patch) |
| continue; |
| |
| if (s->info.stage == MESA_SHADER_FRAGMENT) { |
| get_semantic_ps_outname(var, &semantic); |
| mod->outputs[num_outputs].sysvalue = "TARGET"; |
| } else { |
| const struct glsl_type *type = var->type; |
| if (nir_is_arrayed_io(var, s->info.stage)) |
| type = glsl_get_array_element(type); |
| get_semantic_name(var, &semantic, type); |
| mod->outputs[num_outputs].sysvalue = out_sysvalue_name(var); |
| } |
| next_row = get_additional_semantic_info(s, var, &semantic, next_row, s->info.clip_distance_array_size); |
| |
| mod->info.has_out_position |= semantic.kind== DXIL_SEM_POSITION; |
| mod->info.has_out_depth |= semantic.kind == DXIL_SEM_DEPTH; |
| |
| struct dxil_psv_signature_element *psv_elm = &mod->psv_outputs[num_outputs]; |
| |
| if (!fill_io_signature(mod, num_outputs, &semantic, |
| &mod->outputs[num_outputs], psv_elm)) |
| return; |
| |
| for (unsigned i = 0; i < mod->outputs[num_outputs].num_elements; ++i) { |
| struct dxil_signature_element *elm = &mod->outputs[num_outputs].elements[i]; |
| if (mod->minor_validator <= 4) |
| elm->never_writes_mask = 0xff & ~elm->mask; |
| else |
| /* This will be updated by the module processing */ |
| elm->never_writes_mask = 0xf & ~elm->mask; |
| } |
| |
| ++num_outputs; |
| |
| mod->num_psv_outputs[semantic.stream] = MAX2(mod->num_psv_outputs[semantic.stream], |
| semantic.start_row + semantic.rows); |
| } |
| mod->num_sig_outputs = num_outputs; |
| } |
| |
| static const char * |
| patch_sysvalue_name(nir_variable *var) |
| { |
| switch (var->data.location) { |
| case VARYING_SLOT_TESS_LEVEL_OUTER: |
| switch (glsl_get_aoa_size(var->type)) { |
| case 4: |
| return "QUADEDGE"; |
| case 3: |
| return "TRIEDGE"; |
| case 2: |
| return var->data.location_frac == 0 ? |
| "LINEDET" : "LINEDEN"; |
| default: |
| unreachable("Unexpected outer tess factor array size"); |
| } |
| break; |
| case VARYING_SLOT_TESS_LEVEL_INNER: |
| switch (glsl_get_aoa_size(var->type)) { |
| case 2: |
| return "QUADINT"; |
| case 1: |
| return "TRIINT"; |
| default: |
| unreachable("Unexpected inner tess factory array size"); |
| } |
| break; |
| default: |
| return "NO"; |
| } |
| } |
| |
| static void |
| process_patch_const_signature(struct dxil_module *mod, nir_shader *s) |
| { |
| if (s->info.stage != MESA_SHADER_TESS_CTRL && |
| s->info.stage != MESA_SHADER_TESS_EVAL) |
| return; |
| |
| nir_variable_mode mode = s->info.stage == MESA_SHADER_TESS_CTRL ? |
| nir_var_shader_out : nir_var_shader_in; |
| unsigned num_consts = 0; |
| unsigned next_row = 0; |
| nir_foreach_variable_with_modes(var, s, mode) { |
| struct semantic_info semantic = {0}; |
| if (!var->data.patch) |
| continue; |
| |
| const struct glsl_type *type = var->type; |
| get_semantic_name(var, &semantic, type); |
| |
| mod->patch_consts[num_consts].sysvalue = patch_sysvalue_name(var); |
| next_row = get_additional_semantic_info(s, var, &semantic, next_row, 0); |
| |
| struct dxil_psv_signature_element *psv_elm = &mod->psv_patch_consts[num_consts]; |
| |
| if (!fill_io_signature(mod, num_consts, &semantic, |
| &mod->patch_consts[num_consts], psv_elm)) |
| return; |
| |
| if (mode == nir_var_shader_out) { |
| for (unsigned i = 0; i < mod->patch_consts[num_consts].num_elements; ++i) { |
| struct dxil_signature_element *elm = &mod->patch_consts[num_consts].elements[i]; |
| if (mod->minor_validator <= 4) |
| elm->never_writes_mask = 0xff & ~elm->mask; |
| else |
| /* This will be updated by the module processing */ |
| elm->never_writes_mask = 0xf & ~elm->mask; |
| } |
| } |
| |
| ++num_consts; |
| |
| mod->num_psv_patch_consts = MAX2(mod->num_psv_patch_consts, |
| semantic.start_row + semantic.rows); |
| } |
| mod->num_sig_patch_consts = num_consts; |
| } |
| |
| void |
| preprocess_signatures(struct dxil_module *mod, nir_shader *s, unsigned input_clip_size) |
| { |
| /* DXC does the same: Add an empty string before everything else */ |
| mod->sem_string_table = _mesa_string_buffer_create(mod->ralloc_ctx, 1024); |
| copy_semantic_name_to_string(mod->sem_string_table, ""); |
| |
| process_input_signature(mod, s, input_clip_size); |
| process_output_signature(mod, s); |
| process_patch_const_signature(mod, s); |
| } |
| |
| static const struct dxil_mdnode * |
| get_signature_metadata(struct dxil_module *mod, |
| const struct dxil_signature_record *recs, |
| const struct dxil_psv_signature_element *psvs, |
| unsigned num_elements, |
| bool is_input) |
| { |
| if (num_elements == 0) |
| return NULL; |
| |
| const struct dxil_mdnode *nodes[VARYING_SLOT_MAX]; |
| for (unsigned i = 0; i < num_elements; ++i) { |
| nodes[i] = fill_SV_param_nodes(mod, i, &recs[i], &psvs[i], is_input); |
| } |
| |
| return dxil_get_metadata_node(mod, nodes, num_elements); |
| } |
| |
| const struct dxil_mdnode * |
| get_signatures(struct dxil_module *mod) |
| { |
| const struct dxil_mdnode *input_signature = get_signature_metadata(mod, mod->inputs, mod->psv_inputs, mod->num_sig_inputs, true); |
| const struct dxil_mdnode *output_signature = get_signature_metadata(mod, mod->outputs, mod->psv_outputs, mod->num_sig_outputs, false); |
| const struct dxil_mdnode *patch_const_signature = get_signature_metadata(mod, mod->patch_consts, mod->psv_patch_consts, mod->num_sig_patch_consts, |
| mod->shader_kind == DXIL_DOMAIN_SHADER); |
| |
| const struct dxil_mdnode *SV_nodes[3] = { |
| input_signature, |
| output_signature, |
| patch_const_signature |
| }; |
| if (output_signature || input_signature || patch_const_signature) |
| return dxil_get_metadata_node(mod, SV_nodes, ARRAY_SIZE(SV_nodes)); |
| else |
| return NULL; |
| } |