Copy propagation is translated to index paths, invalidation of variable components is made more precise.
This, together with checking for non-static object references (which are not allowed in HLSL) allows to set object register sizes back to zero without overlapping registers.
I also included implicit array initialization, since, if I remember correctly, what was holding it back was that structs/arrays with object components couldn't be represented with the correct register offsets.
After this, we have to decide if (and how to) move register allocation and register offset calculation to each shader model.
-- v2: vkd3d-shader/hlsl: Support initialization of implicit size arrays. vkd3d-shader/hlsl: Set objects' register size back to 0. vkd3d-shader/hlsl: Check for non-static object references. vkd3d-shader/hlsl: Invalidate components more precisely in copy vkd3d-shader/hlsl: Replace register offsets with index paths in copy vkd3d-shader/hlsl: Print halfs in dump_ir_constant(). vkd3d-shader/hlsl: Skip implicit conversion if types are equal. vkd3d-shader/hlsl: Set component count for objects to 1. tests: Test object references. vkd3d-shader/hlsl: Emit a fixme on complex resource load object derefs. vkd3d-shader/hlsl: Rename 'inner_type' to 'element_type' in function
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com ---
We agreed to call these "elements", and this function is used in following patches, so I think it is better to change it now.
v2: - New in v2. --- libs/vkd3d-shader/hlsl.c | 6 +++--- libs/vkd3d-shader/hlsl.h | 2 +- libs/vkd3d-shader/hlsl_codegen.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index a47e3d2d..01709c8c 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -370,7 +370,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 +423,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); @@ -963,7 +963,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; diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 7970665c..96466ee1 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -735,7 +735,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); diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 36a417e5..cae663cf 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;
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com ---
Otherwise we trigger the assertion with the tests in the next patch. --- libs/vkd3d-shader/hlsl_sm4.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c index cdd73e57..fab2abe2 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);
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- Makefile.am | 1 + tests/object-references.shader_test | 132 ++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 tests/object-references.shader_test
diff --git a/Makefile.am b/Makefile.am index 60db7794..962036ff 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/tests/object-references.shader_test b/tests/object-references.shader_test new file mode 100644 index 00000000..a9fbbc50 --- /dev/null +++ b/tests/object-references.shader_test @@ -0,0 +1,132 @@ +[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) + + +[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 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 todo] +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 todo] +// Note: Only valid in shader model 5.1 +Texture2D tex[3]; +uniform int n; + +float4 main() : sv_target +{ + return tex[n].Load(0); +}
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- v2: - Use switch statement in hlsl_type_component_count(). --- libs/vkd3d-shader/hlsl.c | 41 +++++++++++++--------- tests/hlsl-initializer-objects.shader_test | 4 +-- 2 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 01709c8c..e5b2e8e4 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -548,25 +548,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; + } + + case HLSL_CLASS_ARRAY: + return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_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_OBJECT: + return 1; + + default: + assert(0); + return 0; + } }
bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2) diff --git a/tests/hlsl-initializer-objects.shader_test b/tests/hlsl-initializer-objects.shader_test index 2306d07f..70f060c2 100644 --- a/tests/hlsl-initializer-objects.shader_test +++ b/tests/hlsl-initializer-objects.shader_test @@ -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
From: Francisco Casas fcasas@codeweavers.com
Otherwise we get false in implicit_compatible_data_types() when passing types that are equal but not convertible according to convertible_data_type(); e.g. getting:
"Can't implicitly convert from Texture2D<float4> to Texture2D<float4>."
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- libs/vkd3d-shader/hlsl.y | 3 +++ tests/hlsl-initializer-objects.shader_test | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 7df7afb9..1e488afa 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; diff --git a/tests/hlsl-initializer-objects.shader_test b/tests/hlsl-initializer-objects.shader_test index 70f060c2..dcb60029 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]
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- v2: - Handling halves identically to floats. --- libs/vkd3d-shader/hlsl.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index e5b2e8e4..b27bdda3 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1545,6 +1545,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;
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com ---
It was necessary to add ``` while (transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL)); ``` just after transforming deref paths back into offsets, so that the constant register offset expressions could be folded after the constant folding passes.
We should consider precomputing hlsl_type_component_count() in hlsl_type, the same way we currently do with reg_size. Given the many places where this function is called.
In several places I renamed "offset" to either "comp" or "start", depending on the context. I did this to avoid confusion with the register offsets, but it could be arged that the original name is correct, since it can be interpreted as "offset, measured in components" now.
v2: - Using hlsl_get_element_type_from_path_index() instead of hlsl_get_inner_type_from_path_index(). --- libs/vkd3d-shader/hlsl.c | 6 +- libs/vkd3d-shader/hlsl.h | 3 + libs/vkd3d-shader/hlsl_codegen.c | 178 ++++++++++++++++++++++--------- 3 files changed, 135 insertions(+), 52 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index b27bdda3..b3c02e18 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -770,7 +770,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;
@@ -1036,8 +1036,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; diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 96466ee1..d63fef97 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -721,6 +721,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); @@ -821,6 +822,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_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index cae663cf..9e234ca3 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -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,32 @@ 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) { + unsigned int component_count = hlsl_type_component_count(var_def->var->data_type); unsigned i;
TRACE("Invalidate variable %s.\n", var_def->var->name);
- for (i = 0; i < var_def->var->data_type->reg_size; ++i) + for (i = 0; i < component_count; ++i) var_def->values[i].state = VALUE_STATE_DYNAMICALLY_WRITTEN; }
-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 +627,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 +700,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 +718,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,18 +750,18 @@ 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 { @@ -794,13 +796,13 @@ 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; + unsigned int start, count;
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); + if (hlsl_component_index_range_from_deref(ctx, lhs, &start, &count)) + copy_propagation_invalidate_variable(var_def, start, store->writemask); else copy_propagation_invalidate_whole_variable(var_def);
@@ -1996,6 +1998,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 +2183,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 +2197,10 @@ 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);
+ /* 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));
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- v2: - Renamed 'hlsl_type_length' to 'hlsl_type_element_count'. - Use hlsl_get_element_type_from_path_index() instead of hlsl_get_inner_type_from_path_index(). --- libs/vkd3d-shader/hlsl.c | 17 +++++++++ libs/vkd3d-shader/hlsl.h | 1 + libs/vkd3d-shader/hlsl_codegen.c | 63 ++++++++++++++++++++++++++------ 3 files changed, 69 insertions(+), 12 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index b3c02e18..38df7e8a 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) diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index d63fef97..54902c37 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -814,6 +814,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);
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 9e234ca3..7f76fce1 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -606,15 +606,58 @@ static void copy_propagation_invalidate_variable(struct copy_propagation_var_def } }
-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 int component_count = hlsl_type_component_count(var_def->var->data_type); - unsigned i; + unsigned int i, subtype_comp_count; + struct hlsl_ir_node *path_node; + struct hlsl_type *subtype;
- TRACE("Invalidate variable %s.\n", var_def->var->name); + if (depth == deref->path_len) + { + copy_propagation_invalidate_variable(var_def, comp_start, writemask); + return; + }
- for (i = 0; i < component_count; ++i) - var_def->values[i].state = VALUE_STATE_DYNAMICALLY_WRITTEN; + path_node = deref->path[depth].node; + subtype = hlsl_get_element_type_from_path_index(ctx, type, path_node); + + 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); + + 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 comp, @@ -765,7 +808,7 @@ static void copy_propagation_record_store(struct hlsl_ctx *ctx, struct hlsl_ir_s } else { - copy_propagation_invalidate_whole_variable(var_def); + copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask); } }
@@ -796,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 start, count;
if (!(var_def = copy_propagation_create_var_def(ctx, state, var))) continue;
- if (hlsl_component_index_range_from_deref(ctx, lhs, &start, &count)) - copy_propagation_invalidate_variable(var_def, start, store->writemask); - else - copy_propagation_invalidate_whole_variable(var_def); + copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask);
break; }
From: Francisco Casas fcasas@codeweavers.com
It is responsibility of the shader's programmer to ensure that object references can be solved statically.
Resource arrays for ps_5_1 and vs_5_1 are an exception which is not properly handled yet. They probably deserve a different object type.
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- v2: - Renamed to 'check_for_non_static_object_references' to 'validate_static_object_references'. - This function always returns false now. - Print different error messages for each case (resource or sampler). - Implemented note_non_static_deref_expressions() to report to the user the location of the expressions that could not be resolved statically.
For instance, the following shader:
--- 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); } ---
gives the following output:
--- test.hlsl:16:22: E5022: Loaded resource from "s" must be determinable at compile time. test.hlsl:16:12: Expression for loaded resource within "s" cannot be resolved statically. Failed to compile shader, ret -4. ---
- The detection of dynamic object type loads that are not used in resouce_loads (which cause an error in native) is left out of this patch to avoid multiple error messages. --- libs/vkd3d-shader/hlsl_codegen.c | 47 ++++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + tests/object-references.shader_test | 4 +- 3 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 7f76fce1..6c4a0eec 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -967,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); @@ -2236,6 +2281,8 @@ 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)); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 8bde5523..aca5606b 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/object-references.shader_test b/tests/object-references.shader_test index a9fbbc50..bc36777b 100644 --- a/tests/object-references.shader_test +++ b/tests/object-references.shader_test @@ -101,7 +101,7 @@ draw quad probe all rgba (2132, 2132, 2132, 1111)
-[pixel shader fail todo] +[pixel shader fail] Texture2D tex[3]; uniform int n;
@@ -121,7 +121,7 @@ float4 main() : sv_target }
-[pixel shader fail todo] +[pixel shader fail] // Note: Only valid in shader model 5.1 Texture2D tex[3]; uniform int n;
From: Francisco Casas fcasas@codeweavers.com
Signed-off-by: Francisco Casas fcasas@codeweavers.com --- libs/vkd3d-shader/hlsl.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 38df7e8a..f3386e4c 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -198,7 +198,6 @@ 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) type->reg_size = (type->e.array.elements_count - 1) * align(element_size, 4) + element_size; else @@ -218,8 +217,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; @@ -230,8 +227,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; } }
From: Francisco Casas fcasas@codeweavers.com
HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT (zero) is used as a temporal value for elements_count for implicit size arrays. This value is replaced by the correct one after parsing the initializer.
In case the implicit array is not initialized correctly, hlsl_error() is called but the array size is kept at 0. So the rest of the code must handle these cases.
In shader model 5.1, unlike in 5.0, declaring a multi-dimensional object-type array with the last dimension implicit results in an error. This happens even in presence of an initializer.
So, both gen_struct_fields() and declare_vars() first check if the shader model is 5.1, the array elements are objects, and if there is at least one implicit array size to handle the whole type as an unbounded resource array.
Signed-off-by: Francisco Casas fcasas@codeweavers.com
--- v2: - Detection of incorrect use of implicit arrays was moved from the parser rules to the respective add_* functions. v3: - Replaced 'if' with 'elif' in hlsl_type_calculate_reg_size(). - Updated changes to the new implicit array tests. - Removed incorrect empty line. - Fixed typo in error. - Removed field_size assertion in hlsl_type_calculate_reg_size(). v4: - Drop variables and struct fields declared as unbounded resource arrays. v5: - Renamed 'implicit arrays' to 'implicit size arrays'. v6: - Use 'true' instead of '1'. - Reordered some error detection checks. - Checking for shader model 5.1. v7: - Checking for unbounded_res_array first, and then branching, in gen_struct_fields() and declare_vars(). --- libs/vkd3d-shader/hlsl.c | 11 +- libs/vkd3d-shader/hlsl.h | 2 + libs/vkd3d-shader/hlsl.y | 144 +++++++++++++++++- ...lsl-initializer-implicit-array.shader_test | 16 +- 4 files changed, 158 insertions(+), 15 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index f3386e4c..2b5222db 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -198,7 +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;
- 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; @@ -1313,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; }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 54902c37..d3317bb1 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; diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 1e488afa..91f0ab60 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -853,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) { @@ -870,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; @@ -916,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); @@ -1775,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) @@ -3517,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 @@ -4058,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/tests/hlsl-initializer-implicit-array.shader_test b/tests/hlsl-initializer-implicit-array.shader_test index 648825eb..38c8234c 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]