I am still working on parsing the remaining features of the effects framework, such as the FX functions for the state block entries -- such as SetBlendState() -- and the "compile" and "Compileshader()" syntax. However, after adding the many tests included in 2/7 and reading the feedback from !708, I think that this first batch of patches are going in the right direction in terms of parsing the state blocks and how to represent them internally.
As Nikolay mentioned in https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/708#note_64421 and https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/708#note_64444 there are many types of state blocks entries, which should be identified for writing the effect metadata.
A part that may cause discussion on this series is that I kept the representation of `struct hlsl_state_block_entry` using a `hlsl_block` to keep it general enough to represent all these types of state block entries, thinking on later implementing a helper to identify which type of entry we are dealing with.
Even though, as Nikolay pointed out, the instruction set of fx shaders is different, I still think that HLSL IR should be enough to represent the rhs of state blocks, and hopefully we won't need to pollute it too much (apart from the introduction of hlsl_ir_undeclared_load in 4/7 to avoid creating a new variable) if we find operations that are fx-specific, since I intend to represent calls to FX functions with the same `struct hlsl_state_block_entry`, given that they cannot be called in regular HLSL code. There are many validations that are applied on regular HLSL that still should be applied to state blocks, such as the use of valid swizzles and the correct use of operators.
From: Francisco Casas fcasas@codeweavers.com
Dereferences to PixelShader, VertexShader, RenderTargetView, and DepthStencilView can exist in the program even though they don't use any register set.
This is required before introducing the upcoming tests because otherwise we reach unreacheable code when new_offset_instr_from_deref() calls type_get_regset() on pixel or vertex shaders. --- libs/vkd3d-shader/hlsl.c | 6 ++++++ libs/vkd3d-shader/hlsl.h | 1 + libs/vkd3d-shader/hlsl_codegen.c | 1 + 3 files changed, 8 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index a82334e58..73f01eb9f 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -242,6 +242,12 @@ static enum hlsl_regset type_get_regset(const struct hlsl_type *type) case HLSL_TYPE_UAV: return HLSL_REGSET_UAVS;
+ case HLSL_TYPE_PIXELSHADER: + case HLSL_TYPE_VERTEXSHADER: + case HLSL_TYPE_DEPTHSTENCILVIEW: + case HLSL_TYPE_RENDERTARGETVIEW: + return HLSL_REGSET_NONE; + default: vkd3d_unreachable(); } diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 93ec67d0a..85e080ccb 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -132,6 +132,7 @@ enum hlsl_regset HLSL_REGSET_LAST_OBJECT = HLSL_REGSET_UAVS, HLSL_REGSET_NUMERIC, HLSL_REGSET_LAST = HLSL_REGSET_NUMERIC, + HLSL_REGSET_NONE, };
/* An HLSL source-level data type, including anonymous structs and typedefs. */ diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index ff349ab49..17421e8ef 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -3748,6 +3748,7 @@ static char get_regset_name(enum hlsl_regset regset) case HLSL_REGSET_UAVS: return 'u'; case HLSL_REGSET_NUMERIC: + case HLSL_REGSET_NONE: vkd3d_unreachable(); } vkd3d_unreachable();
From: Francisco Casas fcasas@codeweavers.com
--- Makefile.am | 1 + tests/hlsl/fx-syntax.shader_test | 578 +++++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 tests/hlsl/fx-syntax.shader_test
diff --git a/Makefile.am b/Makefile.am index a9ea14338..0d3ca17a3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -106,6 +106,7 @@ vkd3d_shader_tests = \ tests/hlsl/function-return.shader_test \ tests/hlsl/function.shader_test \ tests/hlsl/fwidth.shader_test \ + tests/hlsl/fx-syntax.shader_test \ tests/hlsl/gather-offset.shader_test \ tests/hlsl/gather.shader_test \ tests/hlsl/getdimensions.shader_test \ diff --git a/tests/hlsl/fx-syntax.shader_test b/tests/hlsl/fx-syntax.shader_test new file mode 100644 index 000000000..9e8a78aa6 --- /dev/null +++ b/tests/hlsl/fx-syntax.shader_test @@ -0,0 +1,578 @@ +% For now, these tests are just to check proper parsing of effects syntax. +% Most of these tests are useless as effects. + + +% Arbitrary names are allowed in the lhs of state block entries. +[pixel shader] +sampler sam +{ + Foobar = 3; +}; + +float4 main() : sv_target { return 0; } + + +% Undefined identifiers are allowed in state blocks. +[pixel shader] +sampler sam +{ + Filter = ANISOTROPIC; +}; + +float4 main() : sv_target { return 0; } + + +% Multiple state blocks for array variables are a thing. +[pixel shader todo] +sampler sams[2] +{ + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + } +}; + +float4 main() : sv_target { return 0; } + + +[pixel shader fail(sm<6)] +sampler sams[2] +{ + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + }, // trailing comma not allowed. +}; + +float4 main() : sv_target { return 0; } + + +% Multiple state blocks for multi-dimensional array variables are a thing. +[pixel shader todo] +sampler sams[2][2] +{ + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + } +}; + +float4 main() : sv_target { return 0; } + + +% State blocks cannot be nested further than one level, regardless of multi-dimensionality. +[pixel shader fail(sm<6)] +sampler sams[2][2] +{ + { + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + } + }, + { + { + Filter = ANISOTROPIC; + }, + { + Filter = ANISOTROPIC; + } + } +}; + +float4 main() : sv_target { return 0; } + + +% Arrays of size 1 can still use a single state block without the need to put it inside a list. +[pixel shader] +sampler sams[1] +{ + Filter = ANISOTROPIC; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks may have bracket initializers on the rhs. +[pixel shader todo] +sampler sam +{ + MaxAnisotropy = 3; + cat = {1, 2, {3, "string"}}; + dogs[3] = {1, {2, {4}}, 3, any_identifier}; +}; + +float4 main() : sv_target { return 0; } + + +% Even though using undefined identifiers is allowed, calls to undefined functions are not. +[pixel shader fail(sm<6)] +sampler sam +{ + cat = fun(); +}; + +float4 main() : sv_target { return 0; } + + +% PixelShader and VertexShader are valid identifiers for the lhs +[pixel shader todo] +sampler sam +{ + pixelShader = 20; + PixelShader = 25; + VertexShader = 30; + vertexshader = 35; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for numeric types. +[pixel shader todo] +float f +{ + MaxAnisotropy = 3; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for texture types. +[pixel shader] +Texture2D tex +{ + MaxAnisotropy = 3; +}; + +float4 main() : sv_target { return 0; } + + +% Same rules apply for technique passes +[pixel shader todo] +technique +{ + pass + { + cat = {1, 2, {3, "string"}}; + dogs[3] = {1, {2, {4}}, 3, any_identifier}; + } +} + +float4 main() : sv_target { return 0; } + + +% Multi-dimensional arrays on the lhs on state blocks are syntax errors. +[pixel shader fail(sm<6)] +sampler sam +{ + dogs[1][1] = 1; +}; + +float4 main() : sv_target { return 0; } + +[pixel shader fail(sm<6)] +technique +{ + pass + { + dogs[1][1] = 1; + } +} + +float4 main() : sv_target { return 0; } + + +% Test special "compile" keyword syntax to compile pixel and vertex shaders +[pixel shader todo] +float4 fun() : sv_target +{ + return 0; +} + +sampler sam +{ + cat = compile ps_2_0 fun(); +}; + +float4 main() : sv_target { return 0; } + + +[pixel shader todo] +float4 fun() : sv_target +{ + return 0; +} + +technique +{ + pass + { + cat = compile ps_2_0 fun(); + } +} + +float4 main() : sv_target { return 0; } + + +% Only uniform arguments are expected, even if undefined identifiers are used. +[pixel shader todo] +float4 fun(uniform float4 a, float4 b, uniform float4 c) : sv_target +{ + return a + b + c; +} + +technique +{ + pass + { + cat = compile ps_2_0 fun(foobar, foobar); // Notice 2 arguments, not 3 + } +} + +float4 main() : sv_target { return 0; } + + +[pixel shader fail(sm<6)] +float4 fun(uniform float4 a, float4 b, uniform float4 c) : sv_target +{ + return a + b + c; +} + +technique +{ + pass + { + // passing 3 arguments here is not valid because the function has only 2 uniforms. + cat = compile ps_2_0 fun(1, 2, 3); + } +} + +float4 main() : sv_target { return 0; } + + +% Test effect groups syntax +[pixel shader todo fail(sm>=6)] +fxgroup group1 +{ + technique10 + { + pass + { + TurboEncabulator = prefabulated + aluminite; + malleable = logarithmic - casing; + } + pass pass1 + { + spurving_bearings = pentametric_fan; + hydrocoptic = marzlevanes; + } + } + technique11 tech1 + { + pass + { + Lunar = Waneshaft; + } + } +} + +float4 main() : sv_target { return 0; } + + +% Effect groups cannot have a "technique" without version +[pixel shader fail] +fxgroup group1 +{ + technique + { + } +} + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for DepthStencilState +[pixel shader todo] +DepthStencilState dss1 +{ + DepthEnable = false; + DepthWriteMask = Zero; + DepthFunc = Less; + random_field = 12; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for BlendState. +[pixel shader todo] +BlendState bs1 +{ + random_field = 1; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for VertexShader and PixelShader +[pixel shader] +PixelShader ps1 +{ + random_field = 1; +}; + +VertexShader vs1 +{ + random_field = 1; +}; + +float4 main() : sv_target { return 0; } + + +% State blocks are valid for RasterizerState +[pixel shader todo] +RasterizerState rs +{ + random_field = 1; +}; + +float4 main() : sv_target { return 0; } + + +% Undefined identifiers cannot be indexed. +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +DepthStencilState dss1 +{ + RandomField = foobar[2]; +}; + + +% Undefined identifiers can be swizzled with .x which proves that they are considered scalar +[pixel shader todo] +float4 main() : sv_target { return 0; } + +DepthStencilState dss1 +{ + RandomField = foobar.x; +}; + +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +DepthStencilState dss1 +{ + RandomField = foobar.y; +}; + + +% The type of previously defined variables is respected, but array indexes are not checked. +[pixel shader todo] +float4 arr[3]; + +float4 main() : sv_target { return 0; } + +DepthStencilState dss1 +{ + RandomField = arr[90]; +}; + + +% The type of previously defined variables is respected, and swizzles are checked. +[pixel shader fail(sm<6)] +float3 vec; + +float4 main() : sv_target { return 0; } + +DepthStencilState dss1 +{ + RandomField = vec.w; +}; + + +% Test function call syntax for state blocks. Unlike assignment syntax, only these names are allowed. +% The parameter count is also checked. +[pixel shader todo] +sampler sam +{ + SetBlendState(foo, bar, baz); // 3 parameters + SetDepthStencilState(foo, 2); // 2 parameters + SetRasterizerState(foo); // 1 parameter + SetVertexShader(vs); // 1 parameter + SetDomainShader(ds); // 1 paramter + SetHullShader(100); // 1 parameter + SetGeometryShader(foo + bar); // 1 parameter + SetPixelShader(ps1); // 1 parameter + SetComputeShader("random string"); // 1 parameter + OMSetRenderTargets(RTV0, RTV1, RTV2, RTV3, RTV4, RTV5, RTV6, RTV7, DSV); // 2 to 9 parameters +}; + +float4 main() : sv_target { return 0; } + + +[pixel shader fail(sm<6)] +sampler sam +{ + SetSomeotherState(); +}; + +float4 main() : sv_target { return 0; } + + +% Test the same thing on technique passes +[pixel shader todo] +technique tech1 +{ + pass pass1 + { + SetBlendState(foo, bar, baz); + SetDepthStencilState(foo, 2); + SetRasterizerState(foo); + SetVertexShader(vs); + } +} + +float4 main() : sv_target { return 0; } + + +% It is not allowed to call the functions to set state blocks on the rhs using the assignment syntax +% for state groups or passes. +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +technique +{ + pass + { + cat = SetPixelShader(foobar); + } +} + + +% Test use of a DepthStencilState in SetDepthStencilState(). +[pixel shader todo] +DepthStencilState dss1 +{ + DepthEnable = false; + DepthWriteMask = Zero; + DepthFunc = Less; + foobar_Field = 22; +}; + +float4 main() : sv_target { return 0; } + +technique tech1 +{ + pass pass1 + { + SetDepthStencilState(NULL, dss1); + } +} + + +% It not allowed to call the functions to set states outside state blocks or passes. +[pixel shader fail] +DepthStencilState dss1 +{ + DepthEnable = false; + DepthWriteMask = Zero; + DepthFunc = Less; +}; + +float4 main() : sv_target +{ + SetDepthStencilState(NULL, dss1); + return 0; +} + + +% Test the CompileShader() syntax. +[pixel shader todo fail(sm>=6)] +float arg1, arg2; + +float4 main_vertex(uniform float a) : sv_position { return a; } + +float4 main(uniform float a) : sv_target { return a; } + // ^ dxc expects a semantic here. + +PixelShader ps1 = CompileShader(ps_2_0, main(arg2)); +VertexShader vs1 = CompileShader(vs_2_0, main_vertex(arg1)); + +technique10 tech1 +{ + pass pass1 + { + SetVertexShader(vs1); + SetPixelShader(ps1); + } +} + + +% Undefined identifiers are not allowed if CompileShader() is used outside a state block. +[pixel shader fail] +float4 main(uniform float a) : sv_target { return a; } + +PixelShader ps1 = CompileShader(ps_2_0, main(foobar)); + + +% But undefined identifiers are allowed if inside a state block. +[pixel shader todo fail(sm>=6)] +float4 main_vertex(uniform float a) : sv_position { return a; } + +float4 main(uniform float a) : sv_target { return a; } + // ^ dxc expects a semantic here. + +technique tech1 +{ + pass pass1 + { + SetVertexShader(CompileShader(vs_2_0, main_vertex(foo))); + SetPixelShader(CompileShader(ps_2_0, main(bar))); + } +} + + +% Again only uniform parameters are expected +[pixel shader fail] +float aa, bb; + +float4 main(uniform float a, float b) : sv_target { return a; } + +PixelShader ps1 = CompileShader(ps_2_0, main(aa, bb)); + + +% Only a set of target profiles are allowed +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +PixelShader ps1 = CompileShader(ps_6_0, main()); + +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +PixelShader ps1 = CompileShader(fs_2_0, main()); + + +% Shaders cannot be passed around to another variable: "Initializer must be literal expressions.". +[pixel shader fail(sm<6)] +float4 main() : sv_target { return 0; } + +PixelShader ps1 = CompileShader(ps_2_0, main()); +PixelShader ps2 = ps1; +
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.c | 39 +++++++++++++++++ libs/vkd3d-shader/hlsl.h | 47 ++++++++++++++++++++ libs/vkd3d-shader/hlsl.y | 75 ++++++++++++++++++++++++++++---- tests/hlsl/fx-syntax.shader_test | 2 +- 4 files changed, 154 insertions(+), 9 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 73f01eb9f..b45b3b9a8 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -134,6 +134,26 @@ struct hlsl_ir_var *hlsl_get_var(struct hlsl_scope *scope, const char *name) return hlsl_get_var(scope->upper, name); }
+static void free_state_block_entry(struct hlsl_state_block_entry *entry) +{ + vkd3d_free(entry->name); + vkd3d_free(entry->args); + hlsl_block_cleanup(entry->instrs); + vkd3d_free(entry->instrs); + vkd3d_free(entry); +} + +void hlsl_free_state_block(struct hlsl_state_block *state_block) +{ + unsigned int k; + + assert(state_block); + for (k = 0; k < state_block->count; ++k) + free_state_block_entry(state_block->entries[k]); + vkd3d_free(state_block->entries); + vkd3d_free(state_block); +} + void hlsl_free_var(struct hlsl_ir_var *decl) { unsigned int k; @@ -142,6 +162,11 @@ void hlsl_free_var(struct hlsl_ir_var *decl) hlsl_cleanup_semantic(&decl->semantic); for (k = 0; k <= HLSL_REGSET_LAST_OBJECT; ++k) vkd3d_free((void *)decl->objects_usage[k]); + if (decl->state_block) + { + hlsl_free_state_block(decl->state_block); + decl->state_block = NULL; + } vkd3d_free(decl); }
@@ -3639,6 +3664,20 @@ static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx)
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) + { + LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (var->state_block) + { + hlsl_free_state_block(var->state_block); + var->state_block = NULL; + } + } + } + LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &ctx->scopes, struct hlsl_scope, entry) { LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry) diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 85e080ccb..8b47453b4 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -423,6 +423,10 @@ struct hlsl_ir_var /* Scope that contains annotations for this variable. */ struct hlsl_scope *annotations;
+ /* The state block on the variable's declaration, if any. + * These are only really used for effect profiles. */ + struct hlsl_state_block *state_block; + /* Indexes of the IR instructions where the variable is first written and last read (liveness * range). The IR instructions are numerated starting from 2, because 0 means unused, and 1 * means function entry. */ @@ -458,6 +462,48 @@ struct hlsl_ir_var uint32_t is_separated_resource : 1; };
+/* This struct is used to represent the two main types of state block entries: + * Assignment: + * name = {args[0], args[1], ...}; + * - or - + * name = args[0] + * - or - + * name[lhs_array_size] = args[0] + * - or - + * name[lhs_array_size] = {args[0], args[1], ...}; + * FX function call: + * name(args[0], args[1], ...); + */ +struct hlsl_state_block_entry +{ + bool is_function_call; + + /* For assignments, the name in the lhs. For function calls, the name of the function. */ + char *name; + + /* Whether the lhs in an assignment is an array and, in that case, its size. */ + bool lhs_is_array; + unsigned int lhs_array_size; + + /* For assignments, instructions present in the rhs. For function calls, instructions present + * in the function arguments. */ + struct hlsl_block *instrs; + + /* For assignments, arguments of the rhs initializer. For function calls, the function + * arguments. */ + struct hlsl_ir_node **args; + unsigned int args_count; + + /* For assignments, whether the rhs is wrapped in braces or not. */ + bool rhs_braces; +}; + +struct hlsl_state_block +{ + struct hlsl_state_block_entry **entries; + size_t count, capacity; +}; + /* Sized array of variables representing a function's parameters. */ struct hlsl_func_parameters { @@ -1207,6 +1253,7 @@ void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new); void hlsl_free_attribute(struct hlsl_attribute *attr); void hlsl_free_instr(struct hlsl_ir_node *node); void hlsl_free_instr_list(struct list *list); +void hlsl_free_state_block(struct hlsl_state_block *state_block); void hlsl_free_type(struct hlsl_type *type); void hlsl_free_var(struct hlsl_ir_var *decl);
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index ec8b3d22a..d4646a365 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -77,6 +77,8 @@ struct parse_variable_def struct hlsl_type *basic_type; uint32_t modifiers; struct vkd3d_shader_location modifiers_loc; + + struct hlsl_state_block *state_block; };
struct parse_function @@ -925,6 +927,8 @@ static void free_parse_variable_def(struct parse_variable_def *v) vkd3d_free(v->arrays.sizes); vkd3d_free(v->name); hlsl_cleanup_semantic(&v->semantic); + if (v->state_block) + hlsl_free_state_block(v->state_block); vkd3d_free(v); }
@@ -2349,6 +2353,9 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var free_parse_variable_def(v); continue; } + + var->state_block = v->state_block; + v->state_block = NULL; type = var->data_type;
if (v->initializer.args_count) @@ -5282,6 +5289,16 @@ static void validate_uav_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, hlsl_release_string_buffer(ctx, string); }
+static bool state_block_add_entry(struct hlsl_state_block *state_block, struct hlsl_state_block_entry *entry) +{ + if (!vkd3d_array_reserve((void **)&state_block->entries, &state_block->capacity, state_block->count + 1, + sizeof(*state_block->entries))) + return false; + + state_block->entries[state_block->count++] = entry; + return true; +} + }
%locations @@ -5322,6 +5339,7 @@ static void validate_uav_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, struct parse_attribute_list attr_list; struct hlsl_ir_switch_case *switch_case; struct hlsl_scope *scope; + struct hlsl_state_block *state_block; }
%token KW_BLENDSTATE @@ -5549,6 +5567,8 @@ static void validate_uav_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, %type <variable_def> variable_def %type <variable_def> variable_def_typed
+%type <state_block> state_block + %%
hlsl_prog: @@ -6678,13 +6698,6 @@ variable_decl: $$->reg_reservation = $3.reg_reservation; }
-state: - any_identifier '=' expr ';' - { - vkd3d_free($1); - destroy_block($3); - } - state_block_start: %empty { @@ -6693,7 +6706,52 @@ state_block_start:
state_block: %empty - | state_block state + { + if (!($$ = hlsl_alloc(ctx, sizeof(*$$)))) + YYABORT; + } + | state_block any_identifier '[' C_INTEGER ']' '=' complex_initializer ';' + { + struct hlsl_state_block_entry *entry; + + if (!(entry = hlsl_alloc(ctx, sizeof(*entry)))) + YYABORT; + + entry->is_function_call = false; + + entry->name = $2; + entry->lhs_is_array = true; + entry->lhs_array_size = $4; + + entry->instrs = $7.instrs; + entry->args = $7.args; + entry->args_count = $7.args_count; + entry->rhs_braces = $7.braces; + + $$ = $1; + state_block_add_entry($$, entry); + } + | state_block any_identifier '=' complex_initializer ';' + { + struct hlsl_state_block_entry *entry; + + if (!(entry = hlsl_alloc(ctx, sizeof(*entry)))) + YYABORT; + + entry->is_function_call = false; + + entry->name = $2; + entry->lhs_is_array = false; + entry->lhs_array_size = 0; + + entry->instrs = $4.instrs; + entry->args = $4.args; + entry->args_count = $4.args_count; + entry->rhs_braces = $4.braces; + + $$ = $1; + state_block_add_entry($$, entry); + }
variable_def: variable_decl @@ -6705,6 +6763,7 @@ variable_def: | variable_decl '{' state_block_start state_block '}' { $$ = $1; + $$->state_block = $4; ctx->in_state_block = 0; }
diff --git a/tests/hlsl/fx-syntax.shader_test b/tests/hlsl/fx-syntax.shader_test index 9e8a78aa6..821735f2a 100644 --- a/tests/hlsl/fx-syntax.shader_test +++ b/tests/hlsl/fx-syntax.shader_test @@ -143,7 +143,7 @@ float4 main() : sv_target { return 0; }
% State blocks are valid for numeric types. -[pixel shader todo] +[pixel shader] float f { MaxAnisotropy = 3;
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.c | 50 ++++++++++++++++++++++++++++++++ libs/vkd3d-shader/hlsl.h | 18 ++++++++++++ libs/vkd3d-shader/hlsl.y | 13 ++++----- libs/vkd3d-shader/hlsl_codegen.c | 6 ++++ 4 files changed, 80 insertions(+), 7 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index b45b3b9a8..ebeeedafd 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1591,6 +1591,27 @@ struct hlsl_ir_node *hlsl_new_swizzle(struct hlsl_ctx *ctx, uint32_t s, unsigned return &swizzle->node; }
+struct hlsl_ir_node *hlsl_new_undeclared_load(struct hlsl_ctx *ctx, const char *name, + struct vkd3d_shader_location *loc) +{ + struct hlsl_ir_undeclared_load *load; + struct hlsl_type *type = hlsl_get_scalar_type(ctx, HLSL_TYPE_INT); + + if (!(load = hlsl_alloc(ctx, sizeof(*load)))) + return NULL; + + init_node(&load->node, HLSL_IR_UNDECLARED_LOAD, type, loc); + + if (!(load->name = hlsl_alloc(ctx, strlen(name) + 1))) + { + vkd3d_free(load); + return NULL; + } + strcpy(load->name, name); + + return &load->node; +} + bool hlsl_index_is_noncontiguous(struct hlsl_ir_index *index) { struct hlsl_type *type = index->val.node->data_type; @@ -1911,6 +1932,12 @@ static struct hlsl_ir_node *clone_index(struct hlsl_ctx *ctx, struct clone_instr return dst; }
+static struct hlsl_ir_node *clone_undeclared_load(struct hlsl_ctx *ctx, + struct clone_instr_map *map, struct hlsl_ir_undeclared_load *load) +{ + return hlsl_new_undeclared_load(ctx, load->name, &load->node.loc); +} + void hlsl_free_ir_switch_case(struct hlsl_ir_switch_case *c) { hlsl_block_cleanup(&c->body); @@ -2006,6 +2033,9 @@ static struct hlsl_ir_node *clone_instr(struct hlsl_ctx *ctx,
case HLSL_IR_SWIZZLE: return clone_swizzle(ctx, map, hlsl_ir_swizzle(instr)); + + case HLSL_IR_UNDECLARED_LOAD: + return clone_undeclared_load(ctx, map, hlsl_ir_undeclared_load(instr)); }
vkd3d_unreachable(); @@ -2836,6 +2866,12 @@ static void dump_ir_index(struct vkd3d_string_buffer *buffer, const struct hlsl_ vkd3d_string_buffer_printf(buffer, "]"); }
+static void dump_ir_undeclared_load(struct vkd3d_string_buffer *buffer, + const struct hlsl_ir_undeclared_load *load) +{ + vkd3d_string_buffer_printf(buffer, "%s", load->name); +} + static void dump_ir_switch(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, const struct hlsl_ir_switch *s) { struct hlsl_ir_switch_case *c; @@ -2924,6 +2960,10 @@ static void dump_instr(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, case HLSL_IR_SWIZZLE: dump_ir_swizzle(buffer, hlsl_ir_swizzle(instr)); break; + + case HLSL_IR_UNDECLARED_LOAD: + dump_ir_undeclared_load(buffer, hlsl_ir_undeclared_load(instr)); + break; } }
@@ -3096,6 +3136,12 @@ static void free_ir_index(struct hlsl_ir_index *index) vkd3d_free(index); }
+static void free_ir_undeclared_load(struct hlsl_ir_undeclared_load *load) +{ + vkd3d_free(load->name); + vkd3d_free(load); +} + void hlsl_free_instr(struct hlsl_ir_node *node) { assert(list_empty(&node->uses)); @@ -3153,6 +3199,10 @@ void hlsl_free_instr(struct hlsl_ir_node *node) case HLSL_IR_SWITCH: free_ir_switch(hlsl_ir_switch(node)); break; + + case HLSL_IR_UNDECLARED_LOAD: + free_ir_undeclared_load(hlsl_ir_undeclared_load(node)); + break; } }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 8b47453b4..97deb7440 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -299,6 +299,7 @@ enum hlsl_ir_node_type HLSL_IR_STORE, HLSL_IR_SWIZZLE, HLSL_IR_SWITCH, + HLSL_IR_UNDECLARED_LOAD, };
/* Common data for every type of IR instruction node. */ @@ -800,6 +801,15 @@ struct hlsl_ir_constant struct hlsl_reg reg; };
+/* Undeclared loads are load to values found on state blocks or technique passes descriptions, that + * only have a specific value for them and do not concern regular pixel, vertex, or compute + * shaders, except for parsing. */ +struct hlsl_ir_undeclared_load +{ + struct hlsl_ir_node node; + char *name; +}; + struct hlsl_scope { /* Item entry for hlsl_ctx.scopes. */ @@ -1061,6 +1071,12 @@ static inline struct hlsl_ir_switch *hlsl_ir_switch(const struct hlsl_ir_node *n return CONTAINING_RECORD(node, struct hlsl_ir_switch, node); }
+static inline struct hlsl_ir_undeclared_load *hlsl_ir_undeclared_load(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_UNDECLARED_LOAD); + return CONTAINING_RECORD(node, struct hlsl_ir_undeclared_load, node); +} + static inline void hlsl_block_init(struct hlsl_block *block) { list_init(&block->instrs); @@ -1334,6 +1350,8 @@ struct hlsl_type *hlsl_new_struct_type(struct hlsl_ctx *ctx, const char *name, struct hlsl_struct_field *fields, size_t field_count); struct hlsl_ir_node *hlsl_new_swizzle(struct hlsl_ctx *ctx, uint32_t s, unsigned int components, struct hlsl_ir_node *val, const struct vkd3d_shader_location *loc); +struct hlsl_ir_node *hlsl_new_undeclared_load(struct hlsl_ctx *ctx, const char *name, + struct vkd3d_shader_location *loc); 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); struct hlsl_ir_var *hlsl_new_synthetic_var_named(struct hlsl_ctx *ctx, const char *name, diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index d4646a365..015974fe1 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -1297,6 +1297,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str case HLSL_IR_RESOURCE_STORE: case HLSL_IR_STORE: case HLSL_IR_SWITCH: + case HLSL_IR_UNDECLARED_LOAD: hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Expected literal expression."); } @@ -7386,15 +7387,13 @@ primary_expr: { if (ctx->in_state_block) { - struct hlsl_ir_load *load; - struct hlsl_ir_var *var; + struct hlsl_ir_node *load;
- if (!(var = hlsl_new_synthetic_var(ctx, "state_block_expr", - hlsl_get_scalar_type(ctx, HLSL_TYPE_INT), &@1))) + if (!(load = hlsl_new_undeclared_load(ctx, $1, &@1))) YYABORT; - if (!(load = hlsl_new_var_load(ctx, var, &@1))) - YYABORT; - if (!($$ = make_block(ctx, &load->node))) + vkd3d_free($1); + + if (!($$ = make_block(ctx, load))) YYABORT; } else diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 17421e8ef..34b4f1658 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -3706,6 +3706,9 @@ static bool dce(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) case HLSL_IR_RESOURCE_STORE: case HLSL_IR_SWITCH: break; + case HLSL_IR_UNDECLARED_LOAD: + /* Unindentified symbol loads should not appear in the shader program. */ + vkd3d_unreachable(); }
return false; @@ -3817,6 +3820,9 @@ static void compute_liveness_recurse(struct hlsl_block *block, unsigned int loop case HLSL_IR_CALL: /* We should have inlined all calls before computing liveness. */ vkd3d_unreachable(); + case HLSL_IR_UNDECLARED_LOAD: + /* Unindentified symbol loads should not appear in the shader program. */ + vkd3d_unreachable();
case HLSL_IR_STORE: {
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.c | 18 ++++++++++++------ libs/vkd3d-shader/hlsl.h | 7 +++++-- libs/vkd3d-shader/hlsl.y | 32 ++++++++++++++++++++++++++------ tests/hlsl/fx-syntax.shader_test | 4 ++-- 4 files changed, 45 insertions(+), 16 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index ebeeedafd..84bb09aa0 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -156,17 +156,20 @@ void hlsl_free_state_block(struct hlsl_state_block *state_block)
void hlsl_free_var(struct hlsl_ir_var *decl) { + struct hlsl_state_block *state_block, *next_state_block; unsigned int k;
vkd3d_free((void *)decl->name); hlsl_cleanup_semantic(&decl->semantic); for (k = 0; k <= HLSL_REGSET_LAST_OBJECT; ++k) vkd3d_free((void *)decl->objects_usage[k]); - if (decl->state_block) + + LIST_FOR_EACH_ENTRY_SAFE(state_block, next_state_block, &decl->state_blocks, struct hlsl_state_block, entry) { - hlsl_free_state_block(decl->state_block); - decl->state_block = NULL; + hlsl_free_state_block(state_block); } + list_init(&decl->state_blocks); + vkd3d_free(decl); }
@@ -1122,6 +1125,8 @@ struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct } }
+ list_init(&var->state_blocks); + return var; }
@@ -3699,6 +3704,7 @@ static bool hlsl_ctx_init(struct hlsl_ctx *ctx, const struct vkd3d_shader_compil
static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx) { + struct hlsl_state_block *state_block, *next_state_block; struct hlsl_buffer *buffer, *next_buffer; struct hlsl_scope *scope, *next_scope; struct hlsl_ir_var *var, *next_var; @@ -3720,11 +3726,11 @@ static void hlsl_ctx_cleanup(struct hlsl_ctx *ctx) { LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry) { - if (var->state_block) + LIST_FOR_EACH_ENTRY_SAFE(state_block, next_state_block, &var->state_blocks, struct hlsl_state_block, entry) { - hlsl_free_state_block(var->state_block); - var->state_block = NULL; + hlsl_free_state_block(state_block); } + list_init(&var->state_blocks); } }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 97deb7440..5c5bf6531 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -424,9 +424,10 @@ struct hlsl_ir_var /* Scope that contains annotations for this variable. */ struct hlsl_scope *annotations;
- /* The state block on the variable's declaration, if any. + /* A list containing the state block on the variable's declaration, if any. + * An array variable may contain multiple state blocks. * These are only really used for effect profiles. */ - struct hlsl_state_block *state_block; + struct list state_blocks;
/* Indexes of the IR instructions where the variable is first written and last read (liveness * range). The IR instructions are numerated starting from 2, because 0 means unused, and 1 @@ -501,6 +502,8 @@ struct hlsl_state_block_entry
struct hlsl_state_block { + struct list entry; + struct hlsl_state_block_entry **entries; size_t count, capacity; }; diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 015974fe1..5193440b2 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -78,7 +78,7 @@ struct parse_variable_def uint32_t modifiers; struct vkd3d_shader_location modifiers_loc;
- struct hlsl_state_block *state_block; + struct list state_blocks; };
struct parse_function @@ -927,8 +927,7 @@ static void free_parse_variable_def(struct parse_variable_def *v) vkd3d_free(v->arrays.sizes); vkd3d_free(v->name); hlsl_cleanup_semantic(&v->semantic); - if (v->state_block) - hlsl_free_state_block(v->state_block); + assert(list_empty(&v->state_blocks)); vkd3d_free(v); }
@@ -2355,8 +2354,7 @@ static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var continue; }
- var->state_block = v->state_block; - v->state_block = NULL; + list_move_tail(&var->state_blocks, &v->state_blocks); type = var->data_type;
if (v->initializer.args_count) @@ -5469,6 +5467,7 @@ static bool state_block_add_entry(struct hlsl_state_block *state_block, struct h %type <list> variables_def %type <list> variables_def_typed %type <list> switch_cases +%type <list> state_block_list
%token <name> VAR_IDENTIFIER %token <name> NEW_IDENTIFIER @@ -6639,6 +6638,7 @@ type_spec: $$->loc = @1; $$->name = $1; $$->arrays = $2; + list_init(&$$->state_blocks); }
declaration: @@ -6697,6 +6697,7 @@ variable_decl: $$->arrays = $2; $$->semantic = $3.semantic; $$->reg_reservation = $3.reg_reservation; + list_init(&$$->state_blocks); }
state_block_start: @@ -6754,6 +6755,19 @@ state_block: state_block_add_entry($$, entry); }
+state_block_list: + '{' state_block '}' + { + if (!($$ = make_empty_list(ctx))) + YYABORT; + list_add_tail($$, &$2->entry); + } + | state_block_list ',' '{' state_block '}' + { + $$ = $1; + list_add_tail($$, &$4->entry); + } + variable_def: variable_decl | variable_decl '=' complex_initializer @@ -6764,8 +6778,14 @@ variable_def: | variable_decl '{' state_block_start state_block '}' { $$ = $1; - $$->state_block = $4; ctx->in_state_block = 0; + list_add_tail(&$$->state_blocks, &$4->entry); + } + | variable_decl '{' state_block_start state_block_list '}' + { + $$ = $1; + ctx->in_state_block = 0; + list_move_tail(&$$->state_blocks, $4); }
variable_def_typed: diff --git a/tests/hlsl/fx-syntax.shader_test b/tests/hlsl/fx-syntax.shader_test index 821735f2a..5422d6133 100644 --- a/tests/hlsl/fx-syntax.shader_test +++ b/tests/hlsl/fx-syntax.shader_test @@ -23,7 +23,7 @@ float4 main() : sv_target { return 0; }
% Multiple state blocks for array variables are a thing. -[pixel shader todo] +[pixel shader] sampler sams[2] { { @@ -52,7 +52,7 @@ float4 main() : sv_target { return 0; }
% Multiple state blocks for multi-dimensional array variables are a thing. -[pixel shader todo] +[pixel shader] sampler sams[2][2] { {
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.h | 1 + libs/vkd3d-shader/hlsl.y | 7 ++++--- tests/hlsl/fx-syntax.shader_test | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 5c5bf6531..dc738e37f 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -426,6 +426,7 @@ struct hlsl_ir_var
/* A list 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. * These are only really used for effect profiles. */ struct list state_blocks;
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 5193440b2..afa33974e 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -1118,7 +1118,7 @@ static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters }
static bool add_pass(struct hlsl_ctx *ctx, const char *name, struct hlsl_scope *annotations, - const struct vkd3d_shader_location *loc) + struct hlsl_state_block *state_block, const struct vkd3d_shader_location *loc) { struct hlsl_ir_var *var; struct hlsl_type *type; @@ -1127,6 +1127,7 @@ static bool add_pass(struct hlsl_ctx *ctx, const char *name, struct hlsl_scope * if (!(var = hlsl_new_var(ctx, name, type, loc, NULL, 0, NULL))) return false; var->annotations = annotations; + list_add_tail(&var->state_blocks, &state_block->entry);
if (!hlsl_add_var(ctx, var, false)) { @@ -5594,9 +5595,9 @@ name_opt: | any_identifier
pass: - KW_PASS name_opt annotations_opt '{' '}' + KW_PASS name_opt annotations_opt '{' state_block_start state_block '}' { - if (!add_pass(ctx, $2, $3, &@1)) + if (!add_pass(ctx, $2, $3, $6, &@1)) YYABORT; }
diff --git a/tests/hlsl/fx-syntax.shader_test b/tests/hlsl/fx-syntax.shader_test index 5422d6133..4efc53b81 100644 --- a/tests/hlsl/fx-syntax.shader_test +++ b/tests/hlsl/fx-syntax.shader_test @@ -266,7 +266,7 @@ float4 main() : sv_target { return 0; }
% Test effect groups syntax -[pixel shader todo fail(sm>=6)] +[pixel shader fail(sm>=6)] fxgroup group1 { technique10
From: Francisco Casas fcasas@codeweavers.com
--- libs/vkd3d-shader/hlsl.y | 19 ++++++++++++++++++- tests/hlsl/fx-syntax.shader_test | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index afa33974e..a7b48d321 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -5539,6 +5539,7 @@ static bool state_block_add_entry(struct hlsl_state_block *state_block, struct h
%type <name> any_identifier %type <name> var_identifier +%type <name> stateblock_lhs_identifier %type <name> name_opt
%type <parameter> parameter @@ -6701,6 +6702,22 @@ variable_decl: list_init(&$$->state_blocks); }
+stateblock_lhs_identifier: + any_identifier + { + $$ = $1; + } + | KW_PIXELSHADER + { + if (!($$ = hlsl_strdup(ctx, "pixelshader"))) + YYABORT; + } + | KW_VERTEXSHADER + { + if (!($$ = hlsl_strdup(ctx, "vertexshader"))) + YYABORT; + } + state_block_start: %empty { @@ -6734,7 +6751,7 @@ state_block: $$ = $1; state_block_add_entry($$, entry); } - | state_block any_identifier '=' complex_initializer ';' + | state_block stateblock_lhs_identifier '=' complex_initializer ';' { struct hlsl_state_block_entry *entry;
diff --git a/tests/hlsl/fx-syntax.shader_test b/tests/hlsl/fx-syntax.shader_test index 4efc53b81..e17b990cf 100644 --- a/tests/hlsl/fx-syntax.shader_test +++ b/tests/hlsl/fx-syntax.shader_test @@ -130,7 +130,7 @@ float4 main() : sv_target { return 0; }
% PixelShader and VertexShader are valid identifiers for the lhs -[pixel shader todo] +[pixel shader] sampler sam { pixelShader = 20;
Nikolay Sivov (@nsivov) commented about libs/vkd3d-shader/hlsl.h:
uint32_t is_separated_resource : 1;
};
+/* This struct is used to represent the two main types of state block entries:
- Assignment:
name = {args[0], args[1], ...};
- or -
name = args[0]
- or -
name[lhs_array_size] = args[0]
- or -
name[lhs_array_size] = {args[0], args[1], ...};
Here it's index in lhs array "property", not size.