-- v2: vkd3d-shader/hlsl: Reorder default values for matrices. vkd3d-shader/hlsl: Fix numeric register offset for matrix components. tests: Text matrix default value initializers. vkd3d-shader/tpf: Write default values. vkd3d-shader/hlsl: Initialize default values with braceless initializers. vkd3d-shader/hlsl: Record default values for uniforms and constant buffers.
From: Francisco Casas fcasas@codeweavers.com
--- Makefile.am | 1 + tests/hlsl/default-values.shader_test | 82 +++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/hlsl/default-values.shader_test
diff --git a/Makefile.am b/Makefile.am index f823cbc85..15cbfd7f6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -87,6 +87,7 @@ vkd3d_shader_tests = \ tests/hlsl/cross.shader_test \ tests/hlsl/d3dcolor-to-ubyte4.shader_test \ tests/hlsl/ddxddy.shader_test \ + tests/hlsl/default-values.shader_test \ tests/hlsl/determinant.shader_test \ tests/hlsl/discard.shader_test \ tests/hlsl/distance.shader_test \ diff --git a/tests/hlsl/default-values.shader_test b/tests/hlsl/default-values.shader_test new file mode 100644 index 000000000..af724689f --- /dev/null +++ b/tests/hlsl/default-values.shader_test @@ -0,0 +1,82 @@ +[pixel shader todo] +float2 a = {1, 2}; +float4x2 b = {1, 2, 3, 4, 5, 6, 7, 8}; + +float4 main() : sv_target +{ + return float4(a, b[2]); +} + + +[pixel shader fail(sm<6) todo] +float a = 7; +float4 b = a; // initial value must be a literal expression. + +float4 main() : sv_target { return 0; } + + +[pixel shader fail(sm<6) todo] +float a = 7; +float4 b = {1, 2, a, 4}; // initial value must be a literal expression. + +float4 main() : sv_target { return 0; } + + +[pixel shader fail] +Texture2D tex; +struct +{ + Texture2D t; + float a; +} apple = {tex, 4}; // initial value must be a literal expression. + +float4 main() : sv_target +{ + return 0; +} + + +[pixel shader todo] +static const float a = 7; +float4 b = {1, 2, a, 4}; // static constant values are allowed on the initializer. + +float4 main() : sv_target { return 0; } + + +[pixel shader todo] +float2 a = {1, 2}; +float4 b = {3, 5, float2(4, 4)}; // numeric type initializers are allowed. + +float4 main() : sv_target +{ + return 2 * b; +} + + +[pixel shader todo] +struct apple +{ + float3 a[2]; + int2x2 b; +} ap = {1, 2, 3, 4, 5, 6, 7.5, 8, 9, 10}; + +float4 main() : sv_target +{ + return float4(ap.b); +} + + +[require] +shader model >= 5.0 + + +% Default values for doubles don't seem to be saved properly. +[pixel shader todo] +double2 m = {1, 2}; +// double2 m; // Offset: 0 Size: 16 +// = 0x00000000 0x00000000 0x40000000 0x00000000 + +float4 main() : sv_target +{ + return m.y; +}
From: Francisco Casas fcasas@codeweavers.com
--- tests/hlsl/default-values.shader_test | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/tests/hlsl/default-values.shader_test b/tests/hlsl/default-values.shader_test index af724689f..911a683fa 100644 --- a/tests/hlsl/default-values.shader_test +++ b/tests/hlsl/default-values.shader_test @@ -66,6 +66,78 @@ float4 main() : sv_target }
+[pixel shader] +cbuffer buff +{ + float2 a = {1, 2}; + float4x2 b = {1, 2, 3, 4, 5, 6, 7, 8}; +} + +float4 main() : sv_target +{ + return float4(a, b[2]); +} + + +[pixel shader fail(sm<6) todo] +cbuffer buff +{ + float a = 7; + float4 b = a; // initial value must be a literal expression. +} + +float4 main() : sv_target { return 0; } + + +[pixel shader fail(sm<6) todo] +cbuffer buff +{ + float a = 7; + float4 b = {1, 2, a, 4}; // initial value must be a literal expression. +} + +float4 main() : sv_target { return 0; } + + +[pixel shader] +static const float a = 7; + +cbuffer buff +{ + float4 b = {1, 2, a, 4}; // static constant values are allowed on the initializer. +} + +float4 main() : sv_target { return 0; } + + +[pixel shader] +cbuffer buff +{ + float2 a = {1, 2}; + float4 b = {3, 5, float2(4, 4)}; // numeric type initializers are allowed. +} + +float4 main() : sv_target +{ + return 2 * b; +} + + +[pixel shader] +cbuffer buff +{ + struct apple + { + float3 a[2]; + int2x2 b; + } ap = {1, 2, 3, 4, 5, 6, 7.5, 8, 9, 10}; +} + +float4 main() : sv_target +{ + return float4(ap.b); +} + [require] shader model >= 5.0
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.c | 86 ++++++++++++++++++++++++--- libs/vkd3d-shader/hlsl.h | 32 +++++++--- libs/vkd3d-shader/hlsl.y | 34 ++++++++--- libs/vkd3d-shader/hlsl_codegen.c | 49 ++++++++++++++- libs/vkd3d-shader/tpf.c | 2 + tests/hlsl/default-values.shader_test | 8 +-- 6 files changed, 181 insertions(+), 30 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index ed80e2b75..075eccf8a 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -163,6 +163,15 @@ void hlsl_free_var(struct hlsl_ir_var *decl) for (k = 0; k <= HLSL_REGSET_LAST_OBJECT; ++k) vkd3d_free((void *)decl->objects_usage[k]);
+ if (decl->default_values) + { + unsigned int component_count = hlsl_type_component_count(decl->data_type); + + for (k = 0; k < component_count; ++k) + hlsl_src_remove(&decl->default_values[k].src); + vkd3d_free(decl->default_values); + } + for (i = 0; i < decl->state_block_count; ++i) hlsl_free_state_block(decl->state_blocks[i]); vkd3d_free(decl->state_blocks); @@ -1120,6 +1129,26 @@ struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct return var; }
+bool hlsl_init_var_default_values(struct hlsl_ctx *ctx, struct hlsl_ir_var *var) +{ + unsigned int component_count = hlsl_type_component_count(var->data_type); + + assert(!var->default_values); + if (!(var->default_values = hlsl_calloc(ctx, component_count, sizeof(*var->default_values)))) + return false; + return true; +} + +void hlsl_transfer_var_default_values(struct hlsl_ir_var *dst, struct hlsl_ir_var *src) +{ + assert(src->default_values); + assert(!dst->default_values); + assert(hlsl_type_component_count(src->data_type) == hlsl_type_component_count(dst->data_type)); + dst->default_values = src->default_values; + src->default_values = NULL; +} + + struct hlsl_ir_var *hlsl_new_synthetic_var(struct hlsl_ctx *ctx, const char *template, struct hlsl_type *type, const struct vkd3d_shader_location *loc) { @@ -2986,6 +3015,45 @@ void hlsl_dump_function(struct hlsl_ctx *ctx, const struct hlsl_ir_function_decl vkd3d_string_buffer_cleanup(&buffer); }
+void hlsl_dump_var_default_values(const struct hlsl_ir_var *var) +{ + unsigned int k, component_count = hlsl_type_component_count(var->data_type); + struct vkd3d_string_buffer buffer; + + vkd3d_string_buffer_init(&buffer); + if (!var->default_values) + { + vkd3d_string_buffer_printf(&buffer, "var "%s" has no default values.\n", var->name); + vkd3d_string_buffer_trace(&buffer); + vkd3d_string_buffer_cleanup(&buffer); + return; + } + + vkd3d_string_buffer_printf(&buffer, "var "%s" default values:\n", var->name); + for (k = 0; k < component_count; ++k) + { + struct hlsl_default_value *def_value = &var->default_values[k]; + + if (def_value->is_constant) + { + vkd3d_string_buffer_printf(&buffer, " val: 0x%08x\n", def_value->value.u); + } + else if (def_value->src.node) + { + vkd3d_string_buffer_printf(&buffer, " src: "); + dump_src(&buffer, &def_value->src); + vkd3d_string_buffer_printf(&buffer, "\n"); + } + else + { + vkd3d_string_buffer_printf(&buffer, " (unknown)\n"); + } + } + + vkd3d_string_buffer_trace(&buffer); + vkd3d_string_buffer_cleanup(&buffer); +} + void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new) { struct hlsl_src *src, *next; @@ -3725,15 +3793,6 @@ static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx) struct hlsl_type *type, *next_type; unsigned int i;
- hlsl_block_cleanup(&ctx->static_initializers); - - for (i = 0; i < ctx->source_files_count; ++i) - vkd3d_free((void *)ctx->source_files[i]); - vkd3d_free(ctx->source_files); - vkd3d_string_buffer_cache_cleanup(&ctx->string_buffers); - - rb_destroy(&ctx->functions, free_function_rb, NULL); - /* State blocks must be free before the variables, because they contain instructions that may * refer to them. */ LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &ctx->scopes, struct hlsl_scope, entry) @@ -3757,6 +3816,15 @@ static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx) vkd3d_free(scope); }
+ hlsl_block_cleanup(&ctx->static_initializers); + + for (i = 0; i < ctx->source_files_count; ++i) + vkd3d_free((void *)ctx->source_files[i]); + vkd3d_free(ctx->source_files); + vkd3d_string_buffer_cache_cleanup(&ctx->string_buffers); + + rb_destroy(&ctx->functions, free_function_rb, NULL); + LIST_FOR_EACH_ENTRY_SAFE(type, next_type, &ctx->types, struct hlsl_type, entry) hlsl_free_type(type);
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 3cb98b765..2969d5c35 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -402,6 +402,14 @@ struct hlsl_reg_reservation unsigned int offset_index; };
+union hlsl_constant_value_component +{ + uint32_t u; + int32_t i; + float f; + double d; +}; + struct hlsl_ir_var { struct hlsl_type *data_type; @@ -424,6 +432,19 @@ struct hlsl_ir_var /* Scope that contains annotations for this variable. */ struct hlsl_scope *annotations;
+ /* Array of default values the variable was initialized with, one for each component. + * Only for variables that need it, such as uniforms and variables inside constant buffers. + * This value is NULL for others. */ + struct hlsl_default_value + { + /* Pointer to the instruction that contains the default value, only used before it is + * lowered into a constant. */ + struct hlsl_src src; + /* Whether the value could be lowered into a constant, and its value in that case. */ + bool is_constant; + union hlsl_constant_value_component value; + } *default_values; + /* A dynamic array containing the state block on the variable's declaration, if any. * An array variable may contain multiple state blocks. * A technique pass will always contain one. @@ -778,13 +799,7 @@ struct hlsl_ir_constant struct hlsl_ir_node node; struct hlsl_constant_value { - union hlsl_constant_value_component - { - uint32_t u; - int32_t i; - float f; - double d; - } u[4]; + union hlsl_constant_value_component u[4]; } value; /* Constant register of type 'c' where the constant value is stored for SM1. */ struct hlsl_reg reg; @@ -1252,6 +1267,7 @@ void hlsl_block_cleanup(struct hlsl_block *block); bool hlsl_clone_block(struct hlsl_ctx *ctx, struct hlsl_block *dst_block, const struct hlsl_block *src_block);
void hlsl_dump_function(struct hlsl_ctx *ctx, const struct hlsl_ir_function_decl *func); +void hlsl_dump_var_default_values(const struct hlsl_ir_var *var);
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); @@ -1374,6 +1390,8 @@ struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, unsigned struct hlsl_block *body, const struct vkd3d_shader_location *loc); struct hlsl_ir_node *hlsl_new_switch(struct hlsl_ctx *ctx, struct hlsl_ir_node *selector, struct list *cases, const struct vkd3d_shader_location *loc); +bool hlsl_init_var_default_values(struct hlsl_ctx *ctx, struct hlsl_ir_var *var); +void hlsl_transfer_var_default_values(struct hlsl_ir_var *dst, struct hlsl_ir_var *src);
void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5); diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 7fc35d4e1..563577e3d 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2085,9 +2085,16 @@ static void initialize_var_components(struct hlsl_ctx *ctx, struct hlsl_block *i if (!(conv = add_implicit_conversion(ctx, instrs, load, dst_comp_type, &src->loc))) return;
- if (!hlsl_new_store_component(ctx, &block, &dst_deref, *store_index, conv)) - return; - hlsl_block_add_block(instrs, &block); + if (dst->default_values) + { + hlsl_src_from_node(&dst->default_values[*store_index].src, conv); + } + else + { + if (!hlsl_new_store_component(ctx, &block, &dst_deref, *store_index, conv)) + return; + hlsl_block_add_block(instrs, &block); + }
++*store_index; } @@ -2254,7 +2261,8 @@ static void declare_var(struct hlsl_ctx *ctx, struct parse_variable_def *v) } }
- if (!(var = hlsl_new_var(ctx, var_name, type, &v->loc, &new_semantic, modifiers, &v->reg_reservation))) + if (!(var = hlsl_new_var(ctx, var_name, type, &v->loc, &new_semantic, modifiers, + &v->reg_reservation))) { hlsl_cleanup_semantic(&new_semantic); vkd3d_free(var_name); @@ -2385,6 +2393,18 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var
if (v->initializer.args_count) { + bool save_default_values = (ctx->cur_buffer != ctx->globals_buffer) + || (var->storage_modifiers & HLSL_STORAGE_UNIFORM); + + if (save_default_values) + { + if (!(hlsl_init_var_default_values(ctx, var))) + { + free_parse_variable_def(v); + continue; + } + } + if (v->initializer.braces) { unsigned int size = initializer_size(&v->initializer); @@ -2415,7 +2435,7 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var add_assignment(ctx, v->initializer.instrs, &load->node, ASSIGN_OP_ASSIGN, v->initializer.args[0]); }
- if (var->storage_modifiers & HLSL_STORAGE_STATIC) + if (var->default_values || var->storage_modifiers & HLSL_STORAGE_STATIC) hlsl_block_add_block(&ctx->static_initializers, v->initializer.instrs); else hlsl_block_add_block(initializers, v->initializer.instrs); @@ -5636,9 +5656,7 @@ hlsl_prog: | hlsl_prog buffer_declaration buffer_body | hlsl_prog declaration_statement { - if (!list_empty(&$2->instrs)) - hlsl_fixme(ctx, &@2, "Uniform initializer."); - destroy_block($2); + hlsl_block_add_block(&ctx->static_initializers, $2); } | hlsl_prog preproc_directive | hlsl_prog global_technique diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index eaa72836d..96d24cc8f 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -210,14 +210,16 @@ static void prepend_uniform_copy(struct hlsl_ctx *ctx, struct hlsl_block *block, /* Use the synthetic name for the temp, rather than the uniform, so that we * can write the uniform name into the shader reflection data. */
- if (!(uniform = hlsl_new_var(ctx, temp->name, temp->data_type, - &temp->loc, NULL, temp->storage_modifiers, &temp->reg_reservation))) + if (!(uniform = hlsl_new_var(ctx, temp->name, temp->data_type, &temp->loc, NULL, + temp->storage_modifiers, &temp->reg_reservation))) return; list_add_before(&temp->scope_entry, &uniform->scope_entry); list_add_tail(&ctx->extern_vars, &uniform->extern_entry); uniform->is_uniform = 1; uniform->is_param = temp->is_param; uniform->buffer = temp->buffer; + if (temp->default_values) + hlsl_transfer_var_default_values(uniform, temp);
if (!(new_name = hlsl_sprintf_alloc(ctx, "<temp-%s>", temp->name))) return; @@ -2518,6 +2520,48 @@ static bool validate_nonconstant_vector_store_derefs(struct hlsl_ctx *ctx, struc return false; }
+static void lower_var_default_values(struct hlsl_ctx *ctx) +{ + const struct hlsl_scope *scope; + const struct hlsl_ir_var *var; + unsigned k, component_count; + + LIST_FOR_EACH_ENTRY(scope, &ctx->scopes, struct hlsl_scope, entry) + { + LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (!var->default_values) + continue; + + component_count = hlsl_type_component_count(var->data_type); + + for (k = 0; k < component_count; ++k) + { + struct hlsl_default_value *def_value = &var->default_values[k]; + struct hlsl_ir_node *node = def_value->src.node; + + if (!node) + continue; + if (node->type == HLSL_IR_CONSTANT) + { + def_value->is_constant = true; + def_value->value = hlsl_ir_constant(node)->value.u[0]; + } + else + { + /* It is hard to know whether this should be an error or a fixme. + * It depends on whether we have the same capabilities for folding constants as + * the native compliler. */ + hlsl_fixme(ctx, &node->loc, + "Lower default value to a constant for variable "%s", component %u.", + var->name, k); + } + hlsl_src_remove(&def_value->src); + } + } + } +} + /* Lower combined samples and sampler variables to synthesized separated textures and samplers. * That is, translate SM1-style samples in the source to SM4-style samples in the bytecode. */ static bool lower_combined_samples(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) @@ -5465,6 +5509,7 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry lower_ir(ctx, lower_abs, body); }
+ lower_var_default_values(ctx); lower_ir(ctx, validate_nonconstant_vector_store_derefs, body);
do diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index d5019a5dd..f019208f8 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -3534,6 +3534,8 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) put_u32(&buffer, 0); /* type */ put_u32(&buffer, 0); /* FIXME: default value */
+ hlsl_dump_var_default_values(var); + if (profile->major_version >= 5) { put_u32(&buffer, 0); /* texture start */ diff --git a/tests/hlsl/default-values.shader_test b/tests/hlsl/default-values.shader_test index 911a683fa..eef0777bd 100644 --- a/tests/hlsl/default-values.shader_test +++ b/tests/hlsl/default-values.shader_test @@ -1,4 +1,4 @@ -[pixel shader todo] +[pixel shader] float2 a = {1, 2}; float4x2 b = {1, 2, 3, 4, 5, 6, 7, 8};
@@ -36,14 +36,14 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] static const float a = 7; float4 b = {1, 2, a, 4}; // static constant values are allowed on the initializer.
float4 main() : sv_target { return 0; }
-[pixel shader todo] +[pixel shader] float2 a = {1, 2}; float4 b = {3, 5, float2(4, 4)}; // numeric type initializers are allowed.
@@ -53,7 +53,7 @@ float4 main() : sv_target }
-[pixel shader todo] +[pixel shader] struct apple { float3 a[2];
From: Francisco Casas fcasas@codeweavers.com
It is hard to initialize default values on add_assignment() and calling add_assignment() for initializers is not really necessary, we just need the implicit cast. --- libs/vkd3d-shader/hlsl.y | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 563577e3d..526687d0b 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2393,7 +2393,11 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var
if (v->initializer.args_count) { - bool save_default_values = (ctx->cur_buffer != ctx->globals_buffer) + unsigned int store_index = 0; + bool save_default_values; + unsigned int size, k; + + save_default_values = (ctx->cur_buffer != ctx->globals_buffer) || (var->storage_modifiers & HLSL_STORAGE_UNIFORM);
if (save_default_values) @@ -2405,34 +2409,30 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var } }
- if (v->initializer.braces) + if (!v->initializer.braces) { - unsigned int size = initializer_size(&v->initializer); - unsigned int store_index = 0; - unsigned int k; - - if (hlsl_type_component_count(type) != size) + if (!(add_implicit_conversion(ctx, v->initializer.instrs, v->initializer.args[0], type, &v->loc))) { - hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, - "Expected %u components in initializer, but got %u.", - hlsl_type_component_count(type), size); free_parse_variable_def(v); continue; }
- for (k = 0; k < v->initializer.args_count; ++k) - { - initialize_var_components(ctx, v->initializer.instrs, var, - &store_index, v->initializer.args[k]); - } + v->initializer.args[0] = node_from_block(v->initializer.instrs); } - else + + size = initializer_size(&v->initializer); + if (hlsl_type_component_count(type) != size) { - struct hlsl_ir_load *load = hlsl_new_var_load(ctx, var, &var->loc); + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, + "Expected %u components in initializer, but got %u.", + hlsl_type_component_count(type), size); + free_parse_variable_def(v); + continue; + }
- assert(v->initializer.args_count == 1); - hlsl_block_add_instr(v->initializer.instrs, &load->node); - add_assignment(ctx, v->initializer.instrs, &load->node, ASSIGN_OP_ASSIGN, v->initializer.args[0]); + for (k = 0; k < v->initializer.args_count; ++k) + { + initialize_var_components(ctx, v->initializer.instrs, var, &store_index, v->initializer.args[k]); }
if (var->default_values || var->storage_modifiers & HLSL_STORAGE_STATIC)
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/tpf.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index f019208f8..c1d243093 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -3558,6 +3558,34 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) set_u32(&buffer, var_offset, string_offset); write_sm4_type(ctx, &buffer, var->data_type); set_u32(&buffer, var_offset + 4 * sizeof(uint32_t), var->data_type->bytecode_offset); + + if (var->default_values) + { + unsigned int reg_size = var->data_type->reg_size[HLSL_REGSET_NUMERIC]; + unsigned int comp_count = hlsl_type_component_count(var->data_type); + unsigned int default_value_offset; + unsigned int k; + + default_value_offset = bytecode_reserve_bytes(&buffer, reg_size * sizeof(uint32_t)); + set_u32(&buffer, var_offset + 5 * sizeof(uint32_t), default_value_offset); + + for (k = 0; k < comp_count; ++k) + { + struct hlsl_type *comp_type = hlsl_type_get_component_type(ctx, var->data_type, k); + unsigned int comp_offset; + enum hlsl_regset regset; + + comp_offset = hlsl_type_get_component_offset(ctx, var->data_type, k, ®set); + if (regset == HLSL_REGSET_NUMERIC) + { + if (comp_type->base_type == HLSL_TYPE_DOUBLE) + hlsl_fixme(ctx, &var->loc, "Write double default values."); + + set_u32(&buffer, default_value_offset + comp_offset * sizeof(uint32_t), + var->default_values[k].value.u); + } + } + } ++j; } }
From: Francisco Casas fcasas@codeweavers.com
--- tests/hlsl/default-values.shader_test | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)
diff --git a/tests/hlsl/default-values.shader_test b/tests/hlsl/default-values.shader_test index eef0777bd..8723145fe 100644 --- a/tests/hlsl/default-values.shader_test +++ b/tests/hlsl/default-values.shader_test @@ -138,6 +138,81 @@ float4 main() : sv_target return float4(ap.b); }
+ +% For matrices, the values in the default values initializer end up in different components than for +% regular initializers. The values are assigned to the matrix components in Chinese reading order, +% i.e. the components of a whole column are assigned before the next column, unlike regular +% initializers where the components of each row are assigned before the next row. +[pixel shader] +int2x3 m = {1, 2, 3, 4, 5, 6}; +// int2x3 m; // Offset: 0 Size: 40 +// = 0x00000001 0x00000002 0x00000000 0x00000000 +// 0x00000003 0x00000004 0x00000000 0x00000000 +// 0x00000005 0x00000006 + +float4 main() : sv_target +{ + return m[0][0]; +} + + +% The same happens for numeric constructors. +[pixel shader] +int2x3 m = int2x3(1, 2, 3, 4, 5, 6); +// int2x3 m; // Offset: 0 Size: 40 +// = 0x00000001 0x00000002 0x00000000 0x00000000 +// 0x00000003 0x00000004 0x00000000 0x00000000 +// 0x00000005 0x00000006 + +float4 main() : sv_target +{ + return m[0][0]; +} + + +[pixel shader] +cbuffer buff +{ + row_major int2x3 m = {1, 2, 3, 4, 5, 6}; + // row_major int2x3 m; // Offset: 0 Size: 28 + // = 0x00000001 0x00000003 0x00000005 0x00000000 + // 0x00000002 0x00000004 0x00000006 +} + +float4 main() : sv_target +{ + return m[0][0]; +} + + +[pixel shader fail(sm>=6)] +struct +{ + float2 a[2]; + row_major int2x3 b; + float3 c[2]; +} apple = {100, 101, 110, 111, 1, 2, 3, 4, 5, 6, 200, 201, 202, 210, 211, 212}; +// struct +// { +// +// float2 a[2]; // Offset: 0 +// row_major int2x3 b; // Offset: 32 +// float3 c[2]; // Offset: 64 +// +// } apple; // Offset: 0 Size: 92 +// = 0x42c80000 0x42ca0000 0x00000000 0x00000000 +// 0x42dc0000 0x42de0000 0x00000000 0x00000000 +// 0x00000001 0x00000003 0x00000005 0x00000000 +// 0x00000002 0x00000004 0x00000006 0x00000000 +// 0x43480000 0x43490000 0x434a0000 0x00000000 +// 0x43520000 0x43530000 0x43540000 + +float4 main() : sv_target +{ + return float4(apple.c[0], 0); +} + + [require] shader model >= 5.0
From: Francisco Casas fcasas@codeweavers.com
---
Honestly I don't know when this broke, but seems we were only using this function for resources until now that we are writing default values. --- libs/vkd3d-shader/hlsl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 075eccf8a..8eb0be1f3 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -509,12 +509,14 @@ unsigned int hlsl_type_get_component_offset(struct hlsl_ctx *ctx, struct hlsl_ty
switch (type->class) { - case HLSL_CLASS_SCALAR: case HLSL_CLASS_VECTOR: - case HLSL_CLASS_MATRIX: offset[HLSL_REGSET_NUMERIC] += idx; break;
+ case HLSL_CLASS_MATRIX: + offset[HLSL_REGSET_NUMERIC] += 4 * idx; + break; + case HLSL_CLASS_STRUCT: for (r = 0; r <= HLSL_REGSET_LAST; ++r) offset[r] += type->e.record.fields[idx].reg_offset[r]; @@ -534,6 +536,7 @@ unsigned int hlsl_type_get_component_offset(struct hlsl_ctx *ctx, struct hlsl_ty assert(idx == 0); break;
+ case HLSL_CLASS_SCALAR: default: vkd3d_unreachable(); }
From: Francisco Casas fcasas@codeweavers.com
Default value initializers behave differently than regular initializers for matrices.
While regular initializers assign the rhs elements in reading-order (completing one row at the time), default initializers assing the rhs elements in Chinese reading-order (completing one column at the time).
So after lowering the default values to constants, they are reordered to meet this expectation. This can be done because the default values aren't used elsewhere[1].
Performing this reordering before lowering to constant values implies the need to move the hlsl_default_value in the array, which is not simple because they contain hlsl_src with list nodes.
For reference, compiling this shader:
row_major int2x3 m = {1, 2, 3, 4, 5, 6};
float4 main() : sv_target { return float4(m[0][0], 99, 99, 99); }
gives the following buffer definition:
// cbuffer $Globals // { // // row_major int2x3 m; // Offset: 0 Size: 28 // = 0x00000001 0x00000003 0x00000005 0x00000000 // 0x00000002 0x00000004 0x00000006 // // }
Given that the matrix is column-major, m's default value is actually {{1, 3, 5}, {2, 4, 6}}, unlike the {{1, 2, 3}, {4, 5, 6}} one would expect in a regular initializer.
[1] If for some reason we want to do this before, we could turn var->default_values into an array of struct hlsl_default_value pointers, but seems like an overkill for now. --- libs/vkd3d-shader/hlsl.c | 56 ++++++++++++++++++++++++++++++++ libs/vkd3d-shader/hlsl.h | 1 + libs/vkd3d-shader/hlsl_codegen.c | 4 ++- 3 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 8eb0be1f3..f48f1c7bd 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1151,6 +1151,62 @@ void hlsl_transfer_var_default_values(struct hlsl_ir_var *dst, struct hlsl_ir_va src->default_values = NULL; }
+static void reorder_matrix_default_values(struct hlsl_ir_var *var, int dimx, int dimy, int start) +{ + struct hlsl_default_value aux[16]; + unsigned int i, x, y; + + for (i = 0; i < dimx * dimy; ++i) + { + /* structs that contain list nodes in use should not be copied by value. */ + assert(!var->default_values[start + i].src.node); + aux[i] = var->default_values[start + i]; + } + + for (i = 0; i < dimx * dimy; ++i) + { + y = i / dimx; + x = i % dimx; + var->default_values[start + i] = aux[x * dimy + y]; + } +} + +/* For some reason, for matrices, values from default value initializers end up in different + * components than from regular initializers. Default value initializers fill the matrix in + * Chinese reading order (left-to-right top-to-bottom) instead of regular reading order + * (top-to-bottom left-to-right), so they have to be adjusted. */ +void hlsl_reorder_default_values_in_matrices(struct hlsl_ir_var *var, struct hlsl_type *type, unsigned int start_comp) +{ + unsigned int element_comp_count, i; + + switch (type->class) + { + case HLSL_CLASS_MATRIX: + reorder_matrix_default_values(var, type->dimx, type->dimy, start_comp); + break; + + case HLSL_CLASS_ARRAY: + element_comp_count = hlsl_type_component_count(type->e.array.type); + for (i = 0; i < type->e.array.elements_count; ++i) + { + hlsl_reorder_default_values_in_matrices(var, type->e.array.type, start_comp); + start_comp += element_comp_count; + } + break; + + case HLSL_CLASS_STRUCT: + for (i = 0; i < type->e.record.field_count; ++i) + { + struct hlsl_type *field_type = type->e.record.fields[i].type; + hlsl_reorder_default_values_in_matrices(var, field_type, start_comp); + start_comp += hlsl_type_component_count(field_type); + } + break; + + default: + break; + } +}
struct hlsl_ir_var *hlsl_new_synthetic_var(struct hlsl_ctx *ctx, const char *template, struct hlsl_type *type, const struct vkd3d_shader_location *loc) diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 2969d5c35..224937dec 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -1392,6 +1392,7 @@ struct hlsl_ir_node *hlsl_new_switch(struct hlsl_ctx *ctx, struct hlsl_ir_node * struct list *cases, const struct vkd3d_shader_location *loc); bool hlsl_init_var_default_values(struct hlsl_ctx *ctx, struct hlsl_ir_var *var); void hlsl_transfer_var_default_values(struct hlsl_ir_var *dst, struct hlsl_ir_var *src); +void hlsl_reorder_default_values_in_matrices(struct hlsl_ir_var *var, struct hlsl_type *type, unsigned int start_comp);
void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5); diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 96d24cc8f..f4872b5b5 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -2523,8 +2523,8 @@ static bool validate_nonconstant_vector_store_derefs(struct hlsl_ctx *ctx, struc static void lower_var_default_values(struct hlsl_ctx *ctx) { const struct hlsl_scope *scope; - const struct hlsl_ir_var *var; unsigned k, component_count; + struct hlsl_ir_var *var;
LIST_FOR_EACH_ENTRY(scope, &ctx->scopes, struct hlsl_scope, entry) { @@ -2558,6 +2558,8 @@ static void lower_var_default_values(struct hlsl_ctx *ctx) } hlsl_src_remove(&def_value->src); } + + hlsl_reorder_default_values_in_matrices(var, var->data_type, 0); } } }
:arrow_up: So the pipeline was failing because Zeb's reflection tests in hlsl_d3d12.c detected that I forgot to not write default values for constant buffers variables without initializers (nice!). I was always writing zeros when there was not an initializer. So I fixed that.
Could we get some actual ID3D12ShaderReflection tests for these? I believe the relevant parts of the interface are even implemented; we just need the tests.
Why lower them after parsing? Can't we reuse (most of) evaluate_static_expression_as_uint()?
sm1 has default values as well; if we're not going to write them we should at least have a hlsl_fixme().