From 49caeee1fd59c6e15ef7eb99f31c3bbbed4dd5e5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 4 Jul 2024 17:25:25 -0400 Subject: [PATCH] vkd3d-shader/hlsl: Support default values for function parameters. --- libs/vkd3d-shader/hlsl.y | 127 ++++++++++++++++++++++---- tests/hlsl/function.shader_test | 153 ++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 16 deletions(-) diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 1a635648..55ff8aa9 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -34,6 +34,14 @@ struct parse_fields size_t count, capacity; }; +struct parse_initializer +{ + struct hlsl_ir_node **args; + unsigned int args_count; + struct hlsl_block *instrs; + bool braces; +}; + struct parse_parameter { struct hlsl_type *type; @@ -41,6 +49,7 @@ struct parse_parameter struct hlsl_semantic semantic; struct hlsl_reg_reservation reg_reservation; uint32_t modifiers; + struct parse_initializer initializer; }; struct parse_colon_attribute @@ -49,14 +58,6 @@ struct parse_colon_attribute struct hlsl_reg_reservation reg_reservation; }; -struct parse_initializer -{ - struct hlsl_ir_node **args; - unsigned int args_count; - struct hlsl_block *instrs; - bool braces; -}; - struct parse_array_sizes { uint32_t *sizes; /* innermost first */ @@ -1189,6 +1190,9 @@ static bool add_typedef(struct hlsl_ctx *ctx, struct hlsl_type *const orig_type, return true; } +static void initialize_var_components(struct hlsl_ctx *ctx, struct hlsl_block *instrs, + struct hlsl_ir_var *dst, unsigned int *store_index, struct hlsl_ir_node *src); + static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters *parameters, struct parse_parameter *param, const struct vkd3d_shader_location *loc) { @@ -1205,11 +1209,52 @@ static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "packoffset() is not allowed on function parameters."); + if (parameters->count && parameters->vars[parameters->count - 1]->default_values + && !param->initializer.args_count) + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER, + "Missing default value for parameter '%s'.", param->name); + + if (param->initializer.args_count && (param->modifiers & HLSL_STORAGE_OUT)) + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, + "Output parameter '%s' has a default value.", param->name); + if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, ¶m->semantic, param->modifiers, ¶m->reg_reservation))) return false; var->is_param = 1; + if (param->initializer.args_count) + { + unsigned int component_count = hlsl_type_component_count(param->type); + unsigned int store_index = 0; + unsigned int size, i; + + if (!(var->default_values = hlsl_calloc(ctx, component_count, sizeof(*var->default_values)))) + return false; + + if (!param->initializer.braces) + { + if (!(add_implicit_conversion(ctx, param->initializer.instrs, param->initializer.args[0], param->type, loc))) + return false; + + param->initializer.args[0] = node_from_block(param->initializer.instrs); + } + + size = initializer_size(¶m->initializer); + if (component_count != size) + { + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, + "Expected %u components in initializer, but got %u.", component_count, size); + } + + for (i = 0; i < param->initializer.args_count; ++i) + { + initialize_var_components(ctx, param->initializer.instrs, var, &store_index, param->initializer.args[i]); + } + + free_parse_initializer(¶m->initializer); + } + if (!hlsl_add_var(ctx, var, false)) { hlsl_free_var(var); @@ -2227,7 +2272,9 @@ static bool add_increment(struct hlsl_ctx *ctx, struct hlsl_block *block, bool d /* 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 * vertical 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. */ + * (top-to-bottom left-to-right), so they have to be adjusted. + * An exception is that the order of matrix initializers for function parameters are row-major + * (top-to-bottom left-to-right). */ static unsigned int get_component_index_from_default_initializer_index(struct hlsl_ctx *ctx, struct hlsl_type *type, unsigned int index) { @@ -2300,7 +2347,11 @@ static void initialize_var_components(struct hlsl_ctx *ctx, struct hlsl_block *i return; default_value.value = evaluate_static_expression(ctx, &block, dst_comp_type, &src->loc); - dst_index = get_component_index_from_default_initializer_index(ctx, dst->data_type, *store_index); + if (dst->is_param) + dst_index = *store_index; + else + dst_index = get_component_index_from_default_initializer_index(ctx, dst->data_type, *store_index); + dst->default_values[dst_index] = default_value; hlsl_block_cleanup(&block); @@ -2751,14 +2802,18 @@ static bool func_is_compatible_match(struct hlsl_ctx *ctx, { unsigned int i; - if (decl->parameters.count != args->args_count) + if (decl->parameters.count < args->args_count) return false; - for (i = 0; i < decl->parameters.count; ++i) + for (i = 0; i < args->args_count; ++i) { if (!implicit_compatible_data_types(ctx, args->args[i]->data_type, decl->parameters.vars[i]->data_type)) return false; } + + if (args->args_count < decl->parameters.count && !decl->parameters.vars[args->args_count]->default_values) + return false; + return true; } @@ -2801,11 +2856,11 @@ static bool add_user_call(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fu const struct parse_initializer *args, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *call; - unsigned int i; + unsigned int i, j; - assert(args->args_count == func->parameters.count); + assert(args->args_count <= func->parameters.count); - for (i = 0; i < func->parameters.count; ++i) + for (i = 0; i < args->args_count; ++i) { struct hlsl_ir_var *param = func->parameters.vars[i]; struct hlsl_ir_node *arg = args->args[i]; @@ -2830,11 +2885,40 @@ static bool add_user_call(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fu } } + /* Add default values for the remaining parameters. */ + for (i = args->args_count; i < func->parameters.count; ++i) + { + struct hlsl_ir_var *param = func->parameters.vars[i]; + unsigned int comp_count = hlsl_type_component_count(param->data_type); + struct hlsl_deref param_deref; + + assert(param->default_values); + + hlsl_init_simple_deref_from_var(¶m_deref, param); + + for (j = 0; j < comp_count; ++j) + { + struct hlsl_type *type = hlsl_type_get_component_type(ctx, param->data_type, j); + struct hlsl_constant_value value; + struct hlsl_ir_node *comp; + struct hlsl_block store_block; + + value.u[0] = param->default_values[j].value; + if (!(comp = hlsl_new_constant(ctx, type, &value, loc))) + return false; + hlsl_block_add_instr(args->instrs, comp); + + if (!hlsl_new_store_component(ctx, &store_block, ¶m_deref, j, comp)) + return false; + hlsl_block_add_block(args->instrs, &store_block); + } + } + if (!(call = hlsl_new_call(ctx, func, loc))) return false; hlsl_block_add_instr(args->instrs, call); - for (i = 0; i < func->parameters.count; ++i) + for (i = 0; i < args->args_count; ++i) { struct hlsl_ir_var *param = func->parameters.vars[i]; struct hlsl_ir_node *arg = args->args[i]; @@ -6076,6 +6160,7 @@ static bool state_block_add_entry(struct hlsl_state_block *state_block, struct h %type name_opt %type parameter +%type parameter_decl %type param_list %type parameters @@ -6911,6 +6996,14 @@ param_list: } parameter: + parameter_decl + | parameter_decl '=' complex_initializer + { + $$ = $1; + $$.initializer = $3; + } + +parameter_decl: var_modifiers type_no_void any_identifier arrays colon_attribute { uint32_t modifiers = $1; @@ -6943,6 +7036,8 @@ parameter: $$.name = $3; $$.semantic = $5.semantic; $$.reg_reservation = $5.reg_reservation; + + memset(&$$.initializer, 0, sizeof($$.initializer)); } texture_type: diff --git a/tests/hlsl/function.shader_test b/tests/hlsl/function.shader_test index 29851650..6cfc1e5e 100644 --- a/tests/hlsl/function.shader_test +++ b/tests/hlsl/function.shader_test @@ -345,3 +345,156 @@ export float4 main() : sv_target [test] todo(glsl) draw quad probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) + +% Default parameter values + +[pixel shader] +float func(float a, float b = 2.0f, float c = 3.0f, float d=4.0f) +{ + return a + b + c + d; +} + +float4 main() : sv_target +{ + return float4(func(1.0), func(1.0, 3.0), func(1.0, 3.0, 5.0), func(1.0, 3.0, 5.0, 7.0)); +} + +[test] +todo(glsl) draw quad +probe (0, 0) rgba (10.0, 11.0, 13.0, 16.0) + +[pixel shader] +float4 func(float4 a = 1.0, float4 b = float4(1.0, 2.0, 3.0, 4.0)) +{ + return a + b; +} + +float4 main() : sv_target +{ + return func() + func(float4(-3.0, -4.0, -5.0, -6.0), float4(3.0, 4.0, 5.0, 6.0)); +} + +[test] +todo(glsl) draw quad +probe (0, 0) rgba (2.0, 3.0, 4.0, 5.0) + +[pixel shader fail(sm>=6)] +float4 func(float4 a = 1.0, float4 b = {1.0, 2.0, 3.0, 4.0}) +{ + return a + b; +} + +float4 main() : sv_target +{ + return func() + func(float4(-3.0, -4.0, -5.0, -6.0), float4(3.0, 4.0, 5.0, 6.0)); +} + +[test] +todo(glsl) draw quad +probe (0, 0) rgba (2.0, 3.0, 4.0, 5.0) + + +% For parameters, the order of matrix initializers is row-major. + +[pixel shader] +float4 func(float2x2 m = float2x2(1, 2, 3, 4)) +{ + return float4(m); +} + +float4 main() : sv_target +{ + return func(); +} + +[test] +todo(glsl) draw quad +probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) + +% Missing default value for parameter c. + +[pixel shader fail] +float func(float a, float b = 1.0f, float c) +{ + return a + b + c; +} + +float4 main() : sv_target +{ + return 0; +} + +% Output parameters can't have default values for SM < 6. +% The default values for output parameters are ignored for SM6. + +[pixel shader fail(sm<6)] +float func(float a, out float b = 1.0) +{ + b += a; + return b; +} + +float4 main() : sv_target +{ + return 0; +} + +[pixel shader fail(sm<6)] +float func(float a, out float b = 1.0) +{ + b += a; + return b; +} + +float4 main() : sv_target +{ + float x = 2.0; + float y = func(4.0, x); + return float4(x, y, 0, 0); +} + +[test] +todo(sm>=6 | glsl) draw quad +probe (0, 0) rgba (4.0, 4.0, 0.0, 0.0) + +[pixel shader fail(sm<6)] +float func(float a, inout float b = 1.0) +{ + b += a; + return b; +} + +float4 main() : sv_target +{ + float x = 2.0; + float y = func(4.0, x); + return float4(x, y, 0, 0); +} + +[test] +todo(glsl) draw quad +probe (0, 0) rgba (6.0, 6.0, 0.0, 0.0) + +% Parameters before outputs can't have default values. + +[pixel shader fail] +float func(float a = 1.0f, out float b) +{ + return a; +} + +float4 main() : sv_target +{ + return 0; +} + +[pixel shader fail] +float func(float a = 1.0f, inout float b) +{ + return a; +} + +float4 main() : sv_target +{ + return 0; +}