vkd3d-shader/hlsl: Support default values for function parameters.

This commit is contained in:
Shaun Ren 2024-07-04 17:25:25 -04:00 committed by Henri Verbeet
parent 29699d3d22
commit 49caeee1fd
Notes: Henri Verbeet 2024-07-23 15:43:19 +02:00
Approved-by: Francisco Casas (@fcasas)
Approved-by: Elizabeth Figura (@zfigura)
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/942
2 changed files with 264 additions and 16 deletions

View File

@ -34,6 +34,14 @@ struct parse_fields
size_t count, capacity; 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 parse_parameter
{ {
struct hlsl_type *type; struct hlsl_type *type;
@ -41,6 +49,7 @@ struct parse_parameter
struct hlsl_semantic semantic; struct hlsl_semantic semantic;
struct hlsl_reg_reservation reg_reservation; struct hlsl_reg_reservation reg_reservation;
uint32_t modifiers; uint32_t modifiers;
struct parse_initializer initializer;
}; };
struct parse_colon_attribute struct parse_colon_attribute
@ -49,14 +58,6 @@ struct parse_colon_attribute
struct hlsl_reg_reservation reg_reservation; 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 struct parse_array_sizes
{ {
uint32_t *sizes; /* innermost first */ 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; 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, static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters *parameters,
struct parse_parameter *param, const struct vkd3d_shader_location *loc) 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, hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION,
"packoffset() is not allowed on function parameters."); "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, &param->semantic, param->modifiers, if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, &param->semantic, param->modifiers,
&param->reg_reservation))) &param->reg_reservation)))
return false; return false;
var->is_param = 1; 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(&param->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(&param->initializer);
}
if (!hlsl_add_var(ctx, var, false)) if (!hlsl_add_var(ctx, var, false))
{ {
hlsl_free_var(var); 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 /* 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 * 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 * 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, static unsigned int get_component_index_from_default_initializer_index(struct hlsl_ctx *ctx,
struct hlsl_type *type, unsigned int index) 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; return;
default_value.value = evaluate_static_expression(ctx, &block, dst_comp_type, &src->loc); 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; dst->default_values[dst_index] = default_value;
hlsl_block_cleanup(&block); hlsl_block_cleanup(&block);
@ -2751,14 +2802,18 @@ static bool func_is_compatible_match(struct hlsl_ctx *ctx,
{ {
unsigned int i; unsigned int i;
if (decl->parameters.count != args->args_count) if (decl->parameters.count < args->args_count)
return false; 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)) if (!implicit_compatible_data_types(ctx, args->args[i]->data_type, decl->parameters.vars[i]->data_type))
return false; return false;
} }
if (args->args_count < decl->parameters.count && !decl->parameters.vars[args->args_count]->default_values)
return false;
return true; 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) const struct parse_initializer *args, const struct vkd3d_shader_location *loc)
{ {
struct hlsl_ir_node *call; 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_var *param = func->parameters.vars[i];
struct hlsl_ir_node *arg = args->args[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(&param_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, &param_deref, j, comp))
return false;
hlsl_block_add_block(args->instrs, &store_block);
}
}
if (!(call = hlsl_new_call(ctx, func, loc))) if (!(call = hlsl_new_call(ctx, func, loc)))
return false; return false;
hlsl_block_add_instr(args->instrs, call); 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_var *param = func->parameters.vars[i];
struct hlsl_ir_node *arg = args->args[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> name_opt %type <name> name_opt
%type <parameter> parameter %type <parameter> parameter
%type <parameter> parameter_decl
%type <parameters> param_list %type <parameters> param_list
%type <parameters> parameters %type <parameters> parameters
@ -6911,6 +6996,14 @@ param_list:
} }
parameter: parameter:
parameter_decl
| parameter_decl '=' complex_initializer
{
$$ = $1;
$$.initializer = $3;
}
parameter_decl:
var_modifiers type_no_void any_identifier arrays colon_attribute var_modifiers type_no_void any_identifier arrays colon_attribute
{ {
uint32_t modifiers = $1; uint32_t modifiers = $1;
@ -6943,6 +7036,8 @@ parameter:
$$.name = $3; $$.name = $3;
$$.semantic = $5.semantic; $$.semantic = $5.semantic;
$$.reg_reservation = $5.reg_reservation; $$.reg_reservation = $5.reg_reservation;
memset(&$$.initializer, 0, sizeof($$.initializer));
} }
texture_type: texture_type:

View File

@ -345,3 +345,156 @@ export float4 main() : sv_target
[test] [test]
todo(glsl) draw quad todo(glsl) draw quad
probe (0, 0) rgba (1.0, 2.0, 3.0, 4.0) 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;
}