diff --git a/Makefile.am b/Makefile.am index 60db7794b4b898538cfc0850eac4392551908ca9..962036ff49ea5c7286fba5db2cc9e2980f28a82f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,6 +115,7 @@ vkd3d_shader_tests = \ tests/matrix-semantics.shader_test \ tests/multiple-rt.shader_test \ tests/nointerpolation.shader_test \ + tests/object-references.shader_test tests/pow.shader_test \ tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index a47e3d2d1b30a7fcb93717888b0e51a200dfdcdd..2b5222dbaf36ffcb83d1fa37d871b5f221afded7 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -140,6 +140,23 @@ unsigned int hlsl_type_major_size(const struct hlsl_type *type) return type->dimx; } +unsigned int hlsl_type_element_count(const struct hlsl_type *type) +{ + switch (type->type) + { + case HLSL_CLASS_VECTOR: + return type->dimx; + case HLSL_CLASS_MATRIX: + return hlsl_type_major_size(type); + case HLSL_CLASS_ARRAY: + return type->e.array.elements_count; + case HLSL_CLASS_STRUCT: + return type->e.record.field_count; + default: + return 0; + } +} + static unsigned int get_array_size(const struct hlsl_type *type) { if (type->type == HLSL_CLASS_ARRAY) @@ -181,8 +198,9 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type { unsigned int element_size = type->e.array.type->reg_size; - assert(element_size); - if (is_sm4) + if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + type->reg_size = 0; + else if (is_sm4) type->reg_size = (type->e.array.elements_count - 1) * align(element_size, 4) + element_size; else type->reg_size = type->e.array.elements_count * element_size; @@ -201,8 +219,6 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type struct hlsl_struct_field *field = &type->e.record.fields[i]; unsigned int field_size = field->type->reg_size; - assert(field_size); - type->reg_size = hlsl_type_get_sm4_offset(field->type, type->reg_size); field->reg_offset = type->reg_size; type->reg_size += field_size; @@ -213,8 +229,7 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type } case HLSL_CLASS_OBJECT: - /* For convenience when performing copy propagation. */ - type->reg_size = 1; + type->reg_size = 0; break; } } @@ -370,7 +385,7 @@ static struct hlsl_type *get_type_from_deref(struct hlsl_ctx *ctx, const struct type = deref->var->data_type; for (i = 0; i < deref->path_len; ++i) - type = hlsl_get_inner_type_from_path_index(ctx, type, deref->path[i].node); + type = hlsl_get_element_type_from_path_index(ctx, type, deref->path[i].node); return type; } @@ -423,7 +438,7 @@ static bool init_deref_from_component_index(struct hlsl_ctx *ctx, struct hlsl_bl return true; } -struct hlsl_type *hlsl_get_inner_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type, +struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type, struct hlsl_ir_node *idx) { assert(idx); @@ -548,25 +563,32 @@ struct hlsl_ir_function_decl *hlsl_get_func_decl(struct hlsl_ctx *ctx, const cha unsigned int hlsl_type_component_count(const struct hlsl_type *type) { - unsigned int count = 0, i; - - if (type->type <= HLSL_CLASS_LAST_NUMERIC) - { - return type->dimx * type->dimy; - } - if (type->type == HLSL_CLASS_ARRAY) - { - return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count; - } - if (type->type != HLSL_CLASS_STRUCT) + switch (type->type) { - ERR("Unexpected data type %#x.\n", type->type); - return 0; - } + case HLSL_CLASS_SCALAR: + case HLSL_CLASS_VECTOR: + case HLSL_CLASS_MATRIX: + return type->dimx * type->dimy; + + case HLSL_CLASS_STRUCT: + { + unsigned int count = 0, i; + + for (i = 0; i < type->e.record.field_count; ++i) + count += hlsl_type_component_count(type->e.record.fields[i].type); + return count; + } - for (i = 0; i < type->e.record.field_count; ++i) - count += hlsl_type_component_count(type->e.record.fields[i].type); - return count; + case HLSL_CLASS_ARRAY: + return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count; + + case HLSL_CLASS_OBJECT: + return 1; + + default: + assert(0); + return 0; + } } bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2) @@ -763,7 +785,7 @@ static bool type_is_single_reg(const struct hlsl_type *type) return type->type == HLSL_CLASS_SCALAR || type->type == HLSL_CLASS_VECTOR; } -static bool copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other) +bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other) { unsigned int i; @@ -963,7 +985,7 @@ struct hlsl_ir_load *hlsl_new_load_index(struct hlsl_ctx *ctx, const struct hlsl type = get_type_from_deref(ctx, deref); if (idx) - type = hlsl_get_inner_type_from_path_index(ctx, type, idx); + type = hlsl_get_element_type_from_path_index(ctx, type, idx); if (!(load = hlsl_alloc(ctx, sizeof(*load)))) return NULL; @@ -1029,8 +1051,8 @@ struct hlsl_ir_resource_load *hlsl_new_resource_load(struct hlsl_ctx *ctx, struc return NULL; init_node(&load->node, HLSL_IR_RESOURCE_LOAD, data_type, *loc); load->load_type = type; - copy_deref(ctx, &load->resource, resource); - copy_deref(ctx, &load->sampler, sampler); + hlsl_copy_deref(ctx, &load->resource, resource); + hlsl_copy_deref(ctx, &load->sampler, sampler); hlsl_src_from_node(&load->coords, coords); hlsl_src_from_node(&load->texel_offset, texel_offset); return load; @@ -1293,7 +1315,12 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const stru } for (t = type; t->type == HLSL_CLASS_ARRAY; t = t->e.array.type) - vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count); + { + if (t->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + vkd3d_string_buffer_printf(string, "[]"); + else + vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count); + } return string; } @@ -1538,6 +1565,7 @@ static void dump_ir_constant(struct vkd3d_string_buffer *buffer, const struct hl break; case HLSL_TYPE_FLOAT: + case HLSL_TYPE_HALF: vkd3d_string_buffer_printf(buffer, "%.8e ", value->f); break; diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 7970665c31343dc49e5305684861ccbebf36d0ac..d3317bb12bfb25ba83a085dda8bdf170ceb0a9bc 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -230,6 +230,8 @@ struct hlsl_src #define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR) +#define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0 + struct hlsl_reg_reservation { char type; @@ -721,6 +723,7 @@ void hlsl_dump_function(struct hlsl_ctx *ctx, const struct hlsl_ir_function_decl int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_func, enum vkd3d_shader_target_type target_type, struct vkd3d_shader_code *out); +bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other); void hlsl_cleanup_deref(struct hlsl_deref *deref); void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new); @@ -735,7 +738,7 @@ struct hlsl_ir_function_decl *hlsl_get_func_decl(struct hlsl_ctx *ctx, const cha struct hlsl_type *hlsl_get_type(struct hlsl_scope *scope, const char *name, bool recursive); struct hlsl_ir_var *hlsl_get_var(struct hlsl_scope *scope, const char *name); -struct hlsl_type *hlsl_get_inner_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type, +struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type, struct hlsl_ir_node *idx); struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *basic_type, unsigned int array_size); @@ -813,6 +816,7 @@ struct hlsl_type *hlsl_type_get_component_type(struct hlsl_ctx *ctx, struct hlsl bool hlsl_type_is_row_major(const struct hlsl_type *type); unsigned int hlsl_type_minor_size(const struct hlsl_type *type); unsigned int hlsl_type_major_size(const struct hlsl_type *type); +unsigned int hlsl_type_element_count(const struct hlsl_type *type); unsigned int hlsl_type_get_sm4_offset(const struct hlsl_type *type, unsigned int offset); bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2); @@ -821,6 +825,8 @@ unsigned int hlsl_combine_writemasks(unsigned int first, unsigned int second); unsigned int hlsl_map_swizzle(unsigned int swizzle, unsigned int writemask); unsigned int hlsl_swizzle_from_writemask(unsigned int writemask); +bool hlsl_component_index_range_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, + unsigned int *start, unsigned int *count); bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, unsigned int *offset); unsigned int hlsl_offset_from_deref_safe(struct hlsl_ctx *ctx, const struct hlsl_deref *deref); struct hlsl_reg hlsl_reg_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref); diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 7df7afb940ca38541d6235430839f8f27cec544f..91f0ab60a9ddb9376856532b2b72bd26f5a28e3c 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -370,6 +370,9 @@ static struct hlsl_ir_node *add_implicit_conversion(struct hlsl_ctx *ctx, struct { struct hlsl_type *src_type = node->data_type; + if (hlsl_types_are_equal(src_type, dst_type)) + return node; + if (!implicit_compatible_data_types(src_type, dst_type)) { struct vkd3d_string_buffer *src_string, *dst_string; @@ -850,6 +853,11 @@ static void free_parse_variable_def(struct parse_variable_def *v) vkd3d_free(v); } +static bool shader_is_sm_5_1(const struct hlsl_ctx *ctx) +{ + return ctx->profile->major_version == 5 && ctx->profile->minor_version >= 1; +} + static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields, struct hlsl_type *type, unsigned int modifiers, struct list *defs) { @@ -867,11 +875,45 @@ static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields, LIST_FOR_EACH_ENTRY_SAFE(v, v_next, defs, struct parse_variable_def, entry) { struct hlsl_struct_field *field = &fields->fields[i++]; - unsigned int j; + bool unbounded_res_array = false; + unsigned int k; field->type = type; - for (j = 0; j < v->arrays.count; ++j) - field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[j]); + + if (shader_is_sm_5_1(ctx) && type->type == HLSL_CLASS_OBJECT) + { + for (k = 0; k < v->arrays.count; ++k) + unbounded_res_array |= (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT); + } + + if (unbounded_res_array) + { + if (v->arrays.count == 1) + { + hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays as struct fields."); + free_parse_variable_def(v); + vkd3d_free(field); + continue; + } + else + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Unbounded resource arrays cannot be multi-dimensional."); + } + } + else + { + for (k = 0; k < v->arrays.count; ++k) + { + if (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Implicit size arrays not allowed in struct fields."); + } + + field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[k]); + } + } vkd3d_free(v->arrays.sizes); field->loc = v->loc; field->name = v->name; @@ -913,6 +955,12 @@ static bool add_typedef(struct hlsl_ctx *ctx, DWORD modifiers, struct hlsl_type ret = true; for (i = 0; i < v->arrays.count; ++i) { + if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Implicit size arrays not allowed in typedefs."); + } + if (!(type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]))) { free_parse_variable_def(v); @@ -1772,11 +1820,78 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_t LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) { + bool unbounded_res_array = false; unsigned int i; type = basic_type; - for (i = 0; i < v->arrays.count; ++i) - type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]); + + if (shader_is_sm_5_1(ctx) && type->type == HLSL_CLASS_OBJECT) + { + for (i = 0; i < v->arrays.count; ++i) + unbounded_res_array |= (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT); + } + + if (unbounded_res_array) + { + if (v->arrays.count == 1) + { + hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays."); + free_parse_variable_def(v); + continue; + } + else + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Unbounded resource arrays cannot be multi-dimensional."); + } + } + else + { + for (i = 0; i < v->arrays.count; ++i) + { + if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + { + unsigned int size = initializer_size(&v->initializer); + unsigned int elem_components = hlsl_type_component_count(type); + + if (i < v->arrays.count - 1) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Only innermost array size can be implicit."); + free_parse_initializer(&v->initializer); + v->initializer.args_count = 0; + } + else if (elem_components == 0) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Cannot declare an implicit size array of a size 0 type."); + free_parse_initializer(&v->initializer); + v->initializer.args_count = 0; + } + else if (size == 0) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Implicit size arrays need to be initialized."); + free_parse_initializer(&v->initializer); + v->initializer.args_count = 0; + + } + else if (size % elem_components != 0) + { + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, + "Cannot initialize implicit size array with %u components, expected a multiple of %u.", + size, elem_components); + free_parse_initializer(&v->initializer); + v->initializer.args_count = 0; + } + else + { + v->arrays.sizes[i] = size / elem_components; + } + } + type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]); + } + } vkd3d_free(v->arrays.sizes); if (type->type != HLSL_CLASS_MATRIX) @@ -3514,6 +3629,21 @@ arrays: $$.sizes = new_array; $$.sizes[$$.count++] = size; } + | '[' ']' arrays + { + uint32_t *new_array; + + $$ = $3; + + if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array)))) + { + vkd3d_free($$.sizes); + YYABORT; + } + + $$.sizes = new_array; + $$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT; + } var_modifiers: %empty @@ -4055,7 +4185,14 @@ unary_expr: dst_type = $3; for (i = 0; i < $4.count; ++i) + { + if ($4.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) + { + hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, + "Implicit size arrays not allowed in casts."); + } dst_type = hlsl_new_array_type(ctx, dst_type, $4.sizes[i]); + } if (!compatible_data_types(src_type, dst_type)) { diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 36a417e556a945e1f509e3bd439c1e96eb465f9a..6c4a0eec62debaac07a6375a814a0762bd08f348 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -116,7 +116,7 @@ static struct hlsl_ir_node *new_offset_instr_from_deref(struct hlsl_ctx *ctx, st list_move_tail(&block->instrs, &idx_block.instrs); - type = hlsl_get_inner_type_from_path_index(ctx, type, deref->path[i].node); + type = hlsl_get_element_type_from_path_index(ctx, type, deref->path[i].node); } return offset; @@ -541,7 +541,7 @@ static void copy_propagation_var_def_destroy(struct rb_entry *entry, void *conte } static struct copy_propagation_value *copy_propagation_get_value(const struct copy_propagation_state *state, - const struct hlsl_ir_var *var, unsigned component) + const struct hlsl_ir_var *var, unsigned int component) { for (; state; state = state->parent) { @@ -549,9 +549,11 @@ static struct copy_propagation_value *copy_propagation_get_value(const struct co if (entry) { struct copy_propagation_var_def *var_def = RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry); - enum copy_propagation_value_state state = var_def->values[component].state; + unsigned int component_count = hlsl_type_component_count(var->data_type); + enum copy_propagation_value_state state; - assert(component < var_def->var->data_type->reg_size); + assert(component < component_count); + state = var_def->values[component].state; switch (state) { @@ -573,12 +575,13 @@ static struct copy_propagation_var_def *copy_propagation_create_var_def(struct h { struct rb_entry *entry = rb_get(&state->var_defs, var); struct copy_propagation_var_def *var_def; + unsigned int component_count = hlsl_type_component_count(var->data_type); int res; if (entry) return RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry); - if (!(var_def = hlsl_alloc(ctx, offsetof(struct copy_propagation_var_def, values[var->data_type->reg_size])))) + if (!(var_def = hlsl_alloc(ctx, offsetof(struct copy_propagation_var_def, values[component_count])))) return NULL; var_def->var = var; @@ -590,31 +593,75 @@ static struct copy_propagation_var_def *copy_propagation_create_var_def(struct h } static void copy_propagation_invalidate_variable(struct copy_propagation_var_def *var_def, - unsigned offset, unsigned char writemask) + unsigned int comp, unsigned char writemask) { unsigned i; - TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, offset, debug_hlsl_writemask(writemask)); + TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, comp, debug_hlsl_writemask(writemask)); for (i = 0; i < 4; ++i) { if (writemask & (1u << i)) - var_def->values[offset + i].state = VALUE_STATE_DYNAMICALLY_WRITTEN; + var_def->values[comp + i].state = VALUE_STATE_DYNAMICALLY_WRITTEN; } } -static void copy_propagation_invalidate_whole_variable(struct copy_propagation_var_def *var_def) +static void copy_propagation_invalidate_variable_from_deref_recurse(struct hlsl_ctx *ctx, + struct copy_propagation_var_def *var_def, const struct hlsl_deref *deref, + struct hlsl_type *type, unsigned int depth, unsigned int comp_start, unsigned char writemask) { - unsigned i; + unsigned int i, subtype_comp_count; + struct hlsl_ir_node *path_node; + struct hlsl_type *subtype; + + if (depth == deref->path_len) + { + copy_propagation_invalidate_variable(var_def, comp_start, writemask); + return; + } + + path_node = deref->path[depth].node; + subtype = hlsl_get_element_type_from_path_index(ctx, type, path_node); - TRACE("Invalidate variable %s.\n", var_def->var->name); + if (type->type == HLSL_CLASS_STRUCT) + { + unsigned int idx = hlsl_ir_constant(path_node)->value[0].u; + + for (i = 0; i < idx; ++i) + comp_start += hlsl_type_component_count(type->e.record.fields[i].type); + + copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype, + depth + 1, comp_start, writemask); + } + else + { + subtype_comp_count = hlsl_type_component_count(subtype); - for (i = 0; i < var_def->var->data_type->reg_size; ++i) - var_def->values[i].state = VALUE_STATE_DYNAMICALLY_WRITTEN; + if (path_node->type == HLSL_IR_CONSTANT) + { + copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype, + depth + 1, hlsl_ir_constant(path_node)->value[0].u * subtype_comp_count, writemask); + } + else + { + for (i = 0; i < hlsl_type_element_count(type); ++i) + { + copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype, + depth + 1, i * subtype_comp_count, writemask); + } + } + } +} + +static void copy_propagation_invalidate_variable_from_deref(struct hlsl_ctx *ctx, + struct copy_propagation_var_def *var_def, const struct hlsl_deref *deref, unsigned char writemask) +{ + copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, deref->var->data_type, + 0, 0, writemask); } -static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, unsigned int offset, - unsigned char writemask, struct hlsl_ir_node *node) +static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, unsigned int comp, + unsigned char writemask, struct hlsl_ir_node *instr) { unsigned int i, j = 0; @@ -623,59 +670,56 @@ static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, if (writemask & (1u << i)) { TRACE("Variable %s[%u] is written by instruction %p%s.\n", - var_def->var->name, offset + i, node, debug_hlsl_writemask(1u << i)); - var_def->values[offset + i].state = VALUE_STATE_STATICALLY_WRITTEN; - var_def->values[offset + i].node = node; - var_def->values[offset + i].component = j++; + var_def->var->name, comp + i, instr, debug_hlsl_writemask(1u << i)); + var_def->values[comp + i].state = VALUE_STATE_STATICALLY_WRITTEN; + var_def->values[comp + i].node = instr; + var_def->values[comp + i].component = j++; } } } static struct hlsl_ir_node *copy_propagation_compute_replacement(struct hlsl_ctx *ctx, const struct copy_propagation_state *state, const struct hlsl_deref *deref, - unsigned int count, unsigned int *swizzle) + unsigned int *swizzle) { const struct hlsl_ir_var *var = deref->var; - struct hlsl_ir_node *node = NULL; - unsigned int offset, i; + struct hlsl_ir_node *instr = NULL; + unsigned int start, count, i; - if (!hlsl_offset_from_deref(ctx, deref, &offset)) + if (!hlsl_component_index_range_from_deref(ctx, deref, &start, &count)) return NULL; - if (var->data_type->type != HLSL_CLASS_OBJECT) - assert(offset + count <= var->data_type->reg_size); - *swizzle = 0; for (i = 0; i < count; ++i) { - struct copy_propagation_value *value = copy_propagation_get_value(state, var, offset + i); + struct copy_propagation_value *value = copy_propagation_get_value(state, var, start + i); if (!value) return NULL; - if (!node) + if (!instr) { - node = value->node; + instr = value->node; } - else if (node != value->node) + else if (instr != value->node) { - TRACE("No single source for propagating load from %s[%u-%u].\n", var->name, offset, offset + count); + TRACE("No single source for propagating load from %s[%u-%u].\n", var->name, start, start + count); return NULL; } *swizzle |= value->component << i * 2; } TRACE("Load from %s[%u-%u] propagated as instruction %p%s.\n", - var->name, offset, offset + count, node, debug_hlsl_swizzle(*swizzle, count)); - return node; + var->name, start, start + count, instr, debug_hlsl_swizzle(*swizzle, count)); + return instr; } static bool copy_propagation_transform_load(struct hlsl_ctx *ctx, struct hlsl_ir_load *load, struct copy_propagation_state *state) { - struct hlsl_ir_node *node = &load->node, *new_node; - struct hlsl_type *type = node->data_type; + struct hlsl_ir_node *instr = &load->node, *new_instr; + struct hlsl_type *type = instr->data_type; struct hlsl_ir_swizzle *swizzle_node; unsigned int dimx = 0; unsigned int swizzle; @@ -699,17 +743,17 @@ static bool copy_propagation_transform_load(struct hlsl_ctx *ctx, return false; } - if (!(new_node = copy_propagation_compute_replacement(ctx, state, &load->src, dimx, &swizzle))) + if (!(new_instr = copy_propagation_compute_replacement(ctx, state, &load->src, &swizzle))) return false; if (type->type != HLSL_CLASS_OBJECT) { - if (!(swizzle_node = hlsl_new_swizzle(ctx, swizzle, dimx, new_node, &node->loc))) + if (!(swizzle_node = hlsl_new_swizzle(ctx, swizzle, dimx, new_instr, &instr->loc))) return false; - list_add_before(&node->entry, &swizzle_node->node.entry); - new_node = &swizzle_node->node; + list_add_before(&instr->entry, &swizzle_node->node.entry); + new_instr = &swizzle_node->node; } - hlsl_replace_node(node, new_node); + hlsl_replace_node(instr, new_instr); return true; } @@ -717,17 +761,18 @@ static bool copy_propagation_transform_object_load(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct copy_propagation_state *state) { struct hlsl_ir_load *load; - struct hlsl_ir_node *node; + struct hlsl_ir_node *instr; unsigned int swizzle; - if (!(node = copy_propagation_compute_replacement(ctx, state, deref, 1, &swizzle))) + if (!(instr = copy_propagation_compute_replacement(ctx, state, deref, &swizzle))) return false; /* Only HLSL_IR_LOAD can produce an object. */ - load = hlsl_ir_load(node); - deref->var = load->src.var; - hlsl_src_remove(&deref->offset); - hlsl_src_from_node(&deref->offset, load->src.offset.node); + load = hlsl_ir_load(instr); + + hlsl_cleanup_deref(deref); + hlsl_copy_deref(ctx, deref, &load->src); + return true; } @@ -748,22 +793,22 @@ static void copy_propagation_record_store(struct hlsl_ctx *ctx, struct hlsl_ir_s struct copy_propagation_var_def *var_def; struct hlsl_deref *lhs = &store->lhs; struct hlsl_ir_var *var = lhs->var; - unsigned int offset; + unsigned int start, count; if (!(var_def = copy_propagation_create_var_def(ctx, state, var))) return; - if (hlsl_offset_from_deref(ctx, lhs, &offset)) + if (hlsl_component_index_range_from_deref(ctx, lhs, &start, &count)) { unsigned int writemask = store->writemask; if (store->rhs.node->data_type->type == HLSL_CLASS_OBJECT) writemask = VKD3DSP_WRITEMASK_0; - copy_propagation_set_value(var_def, offset, writemask, store->rhs.node); + copy_propagation_set_value(var_def, start, writemask, store->rhs.node); } else { - copy_propagation_invalidate_whole_variable(var_def); + copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask); } } @@ -794,15 +839,11 @@ static void copy_propagation_invalidate_from_block(struct hlsl_ctx *ctx, struct struct copy_propagation_var_def *var_def; struct hlsl_deref *lhs = &store->lhs; struct hlsl_ir_var *var = lhs->var; - unsigned int offset; if (!(var_def = copy_propagation_create_var_def(ctx, state, var))) continue; - if (hlsl_offset_from_deref(ctx, lhs, &offset)) - copy_propagation_invalidate_variable(var_def, offset, store->writemask); - else - copy_propagation_invalidate_whole_variable(var_def); + copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask); break; } @@ -926,6 +967,51 @@ static bool copy_propagation_execute(struct hlsl_ctx *ctx, struct hlsl_block *bl return progress; } +static void note_non_static_deref_expressions(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, + const char *usage) +{ + unsigned int i; + + for (i = 0; i < deref->path_len; ++i) + { + struct hlsl_ir_node *path_node = deref->path[i].node; + + assert(path_node); + if (path_node->type != HLSL_IR_CONSTANT) + hlsl_note(ctx, &path_node->loc, VKD3D_SHADER_LOG_ERROR, + "Expression for %s within \"%s\" cannot be resolved statically.", + usage, deref->var->name); + } +} + +static bool validate_static_object_references(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, + void *context) +{ + unsigned int start, count; + + if (instr->type == HLSL_IR_RESOURCE_LOAD) + { + struct hlsl_ir_resource_load *load = hlsl_ir_resource_load(instr); + + if (!hlsl_component_index_range_from_deref(ctx, &load->resource, &start, &count)) + { + hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF, + "Loaded resource from \"%s\" must be determinable at compile time.", + load->resource.var->name); + note_non_static_deref_expressions(ctx, &load->resource, "loaded resource"); + } + if (load->sampler.var && !hlsl_component_index_range_from_deref(ctx, &load->sampler, &start, &count)) + { + hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF, + "Resource load sampler from \"%s\" must be determinable at compile time.", + load->sampler.var->name); + note_non_static_deref_expressions(ctx, &load->sampler, "resource load sampler"); + } + } + + return false; +} + static bool is_vec1(const struct hlsl_type *type) { return (type->type == HLSL_CLASS_SCALAR) || (type->type == HLSL_CLASS_VECTOR && type->dimx == 1); @@ -1996,6 +2082,82 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_base_type type) } } +bool hlsl_component_index_range_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, + unsigned int *start, unsigned int *count) +{ + struct hlsl_type *type = deref->var->data_type; + unsigned int i, k; + + *start = 0; + *count = 0; + + for (i = 0; i < deref->path_len; ++i) + { + struct hlsl_ir_node *path_node = deref->path[i].node; + unsigned int idx = 0; + + assert(path_node); + if (path_node->type != HLSL_IR_CONSTANT) + return false; + + /* We should always have generated a cast to UINT. */ + assert(path_node->data_type->type == HLSL_CLASS_SCALAR + && path_node->data_type->base_type == HLSL_TYPE_UINT); + + idx = hlsl_ir_constant(path_node)->value[0].u; + + switch (type->type) + { + case HLSL_CLASS_VECTOR: + if (idx >= type->dimx) + { + hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS, + "Vector index is out of bounds. %u/%u", idx, type->dimx); + return false; + } + *start += idx; + break; + + case HLSL_CLASS_MATRIX: + if (idx >= hlsl_type_major_size(type)) + { + hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS, + "Matrix index is out of bounds. %u/%u", idx, hlsl_type_major_size(type)); + return false; + } + if (hlsl_type_is_row_major(type)) + *start += idx * type->dimx; + else + *start += idx * type->dimy; + break; + + case HLSL_CLASS_ARRAY: + if (idx >= type->e.array.elements_count) + { + hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS, + "Array index is out of bounds. %u/%u", idx, type->e.array.elements_count); + return false; + } + *start += idx * hlsl_type_component_count(type->e.array.type); + break; + + case HLSL_CLASS_STRUCT: + for (k = 0; k < idx; ++k) + *start += hlsl_type_component_count(type->e.record.fields[k].type); + break; + + default: + assert(0); + break; + } + + type = hlsl_get_element_type_from_path_index(ctx, type, path_node); + } + + *count = hlsl_type_component_count(type); + return true; +} + bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, unsigned int *offset) { struct hlsl_ir_node *offset_node = deref->offset.node; @@ -2105,8 +2267,6 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry while (progress); transform_ir(ctx, split_matrix_copies, body, NULL); - transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL); /* TODO: move forward, remove when no longer needed */ - transform_ir(ctx, lower_narrowing_casts, body, NULL); transform_ir(ctx, lower_casts_to_bool, body, NULL); do @@ -2121,6 +2281,12 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry if (ctx->profile->major_version < 4) transform_ir(ctx, lower_division, body, NULL); + transform_ir(ctx, validate_static_object_references, body, NULL); + + /* TODO: move forward, remove when no longer needed */ + transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL); + while (transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL)); + do compute_liveness(ctx, entry_func); while (transform_ir(ctx, dce, body, NULL)); diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c index cdd73e571e3eb50a281b69388014d4c0541be7ce..fab2abe2c45bebcfc17bfa18861082dd636dc0e1 100644 --- a/libs/vkd3d-shader/hlsl_sm4.c +++ b/libs/vkd3d-shader/hlsl_sm4.c @@ -2082,11 +2082,23 @@ static void write_sm4_resource_load(struct hlsl_ctx *ctx, const struct hlsl_ir_node *texel_offset = load->texel_offset.node; const struct hlsl_ir_node *coords = load->coords.node; + if (resource_type->type != HLSL_CLASS_OBJECT) + { + assert(resource_type->type == HLSL_CLASS_ARRAY || resource_type->type == HLSL_CLASS_STRUCT); + hlsl_fixme(ctx, &load->node.loc, "Resource being a component of another variable."); + return; + } + if (load->sampler.var) { const struct hlsl_type *sampler_type = load->sampler.var->data_type; - assert(sampler_type->type == HLSL_CLASS_OBJECT); + if (sampler_type->type != HLSL_CLASS_OBJECT) + { + assert(sampler_type->type == HLSL_CLASS_ARRAY || sampler_type->type == HLSL_CLASS_STRUCT); + hlsl_fixme(ctx, &load->node.loc, "Sampler being a component of another variable."); + return; + } assert(sampler_type->base_type == HLSL_TYPE_SAMPLER); assert(sampler_type->sampler_dim == HLSL_SAMPLER_DIM_GENERIC); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 8bde5523425a8b198e84780aab60eeda9b59a87d..aca5606b0dcfd07ec869f5956c8a1307731a505d 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -118,6 +118,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS = 5019, VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE = 5020, VKD3D_SHADER_ERROR_HLSL_DIVISION_BY_ZERO = 5021, + VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF = 5022, VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION = 5300, VKD3D_SHADER_WARNING_HLSL_DIVISION_BY_ZERO = 5301, diff --git a/tests/hlsl-initializer-implicit-array.shader_test b/tests/hlsl-initializer-implicit-array.shader_test index 648825eb3d6d0e1e3b85deca5141d0015081501e..38c8234cbb54b7c38e2fa375d73fe5e858fdbd84 100644 --- a/tests/hlsl-initializer-implicit-array.shader_test +++ b/tests/hlsl-initializer-implicit-array.shader_test @@ -7,8 +7,8 @@ float4 main() : SV_TARGET } [test] -todo draw quad -todo probe all rgba (50, 60, 70, 80) +draw quad +probe all rgba (50, 60, 70, 80) [pixel shader] @@ -21,8 +21,8 @@ float4 main() : sv_target } [test] -todo draw quad -todo probe all rgba (5.0, 6.0, 7.0, 8.0) +draw quad +probe all rgba (5.0, 6.0, 7.0, 8.0) [pixel shader] @@ -34,8 +34,8 @@ float4 main() : sv_target } [test] -todo draw quad -todo probe all rgba (7.0, 8.0, 9.0, 10.0) +draw quad +probe all rgba (7.0, 8.0, 9.0, 10.0) [pixel shader] @@ -62,8 +62,8 @@ float4 main() : SV_TARGET } [test] -todo draw quad -todo probe all rgba (318.0, 320.0, 322.0, 324.0) +draw quad +probe all rgba (318.0, 320.0, 322.0, 324.0) [pixel shader fail] diff --git a/tests/hlsl-initializer-objects.shader_test b/tests/hlsl-initializer-objects.shader_test index 2306d07f271368f3881d81ab36ed06b1c263fc7e..dcb60029100098bb42770dba74d25e030482bee9 100644 --- a/tests/hlsl-initializer-objects.shader_test +++ b/tests/hlsl-initializer-objects.shader_test @@ -25,8 +25,8 @@ float4 main() : sv_target } [test] -todo draw quad -todo probe all rgba (0.2, 0.2, 0.2, 0.1) +draw quad +probe all rgba (0.2, 0.2, 0.2, 0.1) [pixel shader] @@ -71,7 +71,7 @@ float4 main() : sv_target } -[pixel shader fail todo] +[pixel shader fail] Texture2D tex; struct foo @@ -90,7 +90,7 @@ float4 main() : sv_target } -[pixel shader fail todo] +[pixel shader fail] Texture2D tex; struct foo diff --git a/tests/object-references.shader_test b/tests/object-references.shader_test new file mode 100644 index 0000000000000000000000000000000000000000..6cff351a5f6413970d559d1f753ca736966ba55c --- /dev/null +++ b/tests/object-references.shader_test @@ -0,0 +1,120 @@ +[require] +shader model >= 4.0 + +[texture 0] +size (2, 2) +0.1 0.2 0.3 0.4 0.5 0.7 0.6 0.8 +0.6 0.5 0.2 0.1 0.8 0.0 0.7 1.0 + +[pixel shader] +Texture2D t; + +struct foo { + int3 a; + Texture2D b; +}; + +float4 main(float4 pos : sv_position) : sv_target +{ + struct foo q; + + q.a = int3(pos.xy, 0); + q.b = t; + return q.b.Load(q.a); +} + +[test] +draw quad +probe (0, 0) rgba (0.1, 0.2, 0.3, 0.4) +probe (1, 0) rgba (0.5, 0.7, 0.6, 0.8) +probe (0, 1) rgba (0.6, 0.5, 0.2, 0.1) +probe (1, 1) rgba (0.8, 0.0, 0.7, 1.0) + + +[texture 0] +size (1, 1) +1.0 1.0 1.0 1.0 + +[texture 1] +size (1, 1) +2.0 2.0 2.0 1.0 + +[texture 2] +size (1, 1) +3.0 3.0 3.0 1.0 + +[pixel shader] +Texture2D tex[3]; + +struct foo { + float4 p; + Texture2D t; +}; + +float4 main() : sv_target +{ + struct foo s[3]; + + s[0].t = tex[0]; + s[1].t = tex[1]; + s[2].t = tex[2]; + return 100 * s[2].t.Load(0) + 10 * s[0].t.Load(0) + s[1].t.Load(0); +} + +[test] +todo draw quad +todo probe all rgba (312, 312, 312, 111) + + +[pixel shader] +Texture2D tex1; +Texture2D tex2; +Texture2D tex3; + +float4 main() : sv_target +{ + Texture2D t[3][2]; + + t[0][0] = tex1; // Note: Only invalid in shader model 5.1, array ref. cannot be used as l-value. + t[0][1] = tex2; + t[1][0] = tex3; + t[1][1] = tex1; + t[2][0] = tex2; + t[2][1] = tex3; + return 1000 * t[2][0].Load(0) + 100 * t[1][1].Load(0) + 10 * t[2][1].Load(0) + t[0][1].Load(0); +} + +[test] +draw quad +probe all rgba (2132, 2132, 2132, 1111) + + +[pixel shader fail] +Texture2D tex[3]; +uniform int n; + +struct foo { + float4 p; + Texture2D t; +}; + +float4 main() : sv_target +{ + struct foo s[3]; + + s[0].t = tex[0]; + s[1].t = tex[1]; + s[2].t = tex[2]; + return s[n].t.Load(0); +} + + +[pixel shader fail] +// Note: Only valid in shader model 5.1 +Texture2D tex[3]; +uniform int n; + +float4 main() : sv_target +{ + return tex[n].Load(0); +}