Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
-- v2: vkd3d-shader/hlsl: Initial support for 'switch' statement.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.y | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index fb6d485ea..ab9f81a38 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -6061,7 +6061,19 @@ statement: | loop_statement
jump_statement: - KW_RETURN expr ';' + KW_BREAK ';' + { + struct hlsl_ir_node *jump; + + /* TODO: check that it's used in appropriate context. */ + + if (!($$ = make_empty_block(ctx))) + YYABORT; + if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, NULL, &@1))) + YYABORT; + hlsl_block_add_instr($$, jump); + } + | KW_RETURN expr ';' { $$ = $2; if (!add_return(ctx, $$, node_from_block($$), &@1))
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- Makefile.am | 1 + libs/vkd3d-shader/hlsl.c | 112 ++++++++++++++++++++++ libs/vkd3d-shader/hlsl.h | 25 +++++ libs/vkd3d-shader/hlsl.l | 4 +- libs/vkd3d-shader/hlsl.y | 53 +++++++++++ libs/vkd3d-shader/hlsl_codegen.c | 128 +++++++++++++++++++++++++- libs/vkd3d-shader/tpf.c | 43 +++++++++ libs/vkd3d-shader/vkd3d_shader_main.c | 3 +- tests/hlsl/switch.shader_test | 29 ++++++ 9 files changed, 394 insertions(+), 4 deletions(-) create mode 100644 tests/hlsl/switch.shader_test
diff --git a/Makefile.am b/Makefile.am index 744e46127..9a0c9034e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -163,6 +163,7 @@ vkd3d_shader_tests = \ tests/hlsl/struct-array.shader_test \ tests/hlsl/struct-assignment.shader_test \ tests/hlsl/struct-semantics.shader_test \ + tests/hlsl/switch.shader_test \ tests/hlsl/swizzle-constant-prop.shader_test \ tests/hlsl/swizzle-matrix.shader_test \ tests/hlsl/swizzles.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 5fe9047bf..f2c0c2a6a 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1325,6 +1325,38 @@ struct hlsl_ir_node *hlsl_new_if(struct hlsl_ctx *ctx, struct hlsl_ir_node *cond return &iff->node; }
+struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, struct hlsl_ir_node *value, + struct hlsl_block *block) +{ + struct hlsl_ir_switch_case *c; + + if (!(c = hlsl_alloc(ctx, sizeof(*c)))) + return NULL; + if (value) + hlsl_src_from_node(&c->value, value); + hlsl_block_init(&c->body); + if (block) + hlsl_block_add_block(&c->body, block); + + return c; +} + +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) +{ + struct hlsl_ir_switch *s; + + if (!(s = hlsl_alloc(ctx, sizeof(*s)))) + return NULL; + init_node(&s->node, HLSL_IR_SWITCH, NULL, loc); + hlsl_src_from_node(&s->selector, selector); + list_init(&s->cases); + if (cases) + list_move_head(&s->cases, cases); + + return &s->node; +} + struct hlsl_ir_load *hlsl_new_load_index(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, struct hlsl_ir_node *idx, const struct vkd3d_shader_location *loc) { @@ -1787,6 +1819,29 @@ static struct hlsl_ir_node *clone_index(struct hlsl_ctx *ctx, struct clone_instr return dst; }
+static struct hlsl_ir_node *clone_switch(struct hlsl_ctx *ctx, + struct clone_instr_map *map, struct hlsl_ir_switch *s) +{ + struct hlsl_ir_switch_case *c, *d; + struct hlsl_block body; + struct list cases; + + list_init(&cases); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + clone_block(ctx, &body, &c->body, map); + + if (!(d = hlsl_new_switch_case(ctx, NULL, &body))) + return NULL; + + clone_src(map, &d->value, &c->value); + list_add_tail(&cases, &d->entry); + } + + return hlsl_new_switch(ctx, map_instr(map, s->selector.node), &cases, &s->node.loc); +} + static struct hlsl_ir_node *clone_instr(struct hlsl_ctx *ctx, struct clone_instr_map *map, const struct hlsl_ir_node *instr) { @@ -1825,6 +1880,9 @@ static struct hlsl_ir_node *clone_instr(struct hlsl_ctx *ctx, case HLSL_IR_STORE: return clone_store(ctx, map, hlsl_ir_store(instr));
+ case HLSL_IR_SWITCH: + return clone_switch(ctx, map, hlsl_ir_switch(instr)); + case HLSL_IR_SWIZZLE: return clone_swizzle(ctx, map, hlsl_ir_swizzle(instr)); } @@ -2239,6 +2297,7 @@ const char *hlsl_node_type_to_string(enum hlsl_ir_node_type type) [HLSL_IR_RESOURCE_LOAD ] = "HLSL_IR_RESOURCE_LOAD", [HLSL_IR_RESOURCE_STORE] = "HLSL_IR_RESOURCE_STORE", [HLSL_IR_STORE ] = "HLSL_IR_STORE", + [HLSL_IR_SWITCH ] = "HLSL_IR_SWITCH", [HLSL_IR_SWIZZLE ] = "HLSL_IR_SWIZZLE", };
@@ -2659,6 +2718,35 @@ static void dump_ir_index(struct vkd3d_string_buffer *buffer, const struct hlsl_ vkd3d_string_buffer_printf(buffer, "]"); }
+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; + + vkd3d_string_buffer_printf(buffer, "switch ("); + dump_src(buffer, &s->selector); + vkd3d_string_buffer_printf(buffer, ") {\n"); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + if (c->value.node) + { + vkd3d_string_buffer_printf(buffer, " case "); + dump_src(buffer, &c->value); + vkd3d_string_buffer_printf(buffer, ": {\n"); + } + else + { + vkd3d_string_buffer_printf(buffer, " default: {\n"); + } + + vkd3d_string_buffer_printf(buffer, " "); + dump_block(ctx, buffer, &c->body); + vkd3d_string_buffer_printf(buffer, " };\n"); + } + + vkd3d_string_buffer_printf(buffer, "}\n"); +} + static void dump_instr(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr) { if (instr->index) @@ -2714,6 +2802,10 @@ static void dump_instr(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, dump_ir_store(buffer, hlsl_ir_store(instr)); break;
+ case HLSL_IR_SWITCH: + dump_ir_switch(ctx, buffer, hlsl_ir_switch(instr)); + break; + case HLSL_IR_SWIZZLE: dump_ir_swizzle(buffer, hlsl_ir_swizzle(instr)); break; @@ -2874,6 +2966,22 @@ static void free_ir_swizzle(struct hlsl_ir_swizzle *swizzle) vkd3d_free(swizzle); }
+static void free_ir_switch(struct hlsl_ir_switch *s) +{ + struct hlsl_ir_switch_case *c, *next_c; + + hlsl_src_remove(&s->selector); + + LIST_FOR_EACH_ENTRY_SAFE(c, next_c, &s->cases, struct hlsl_ir_switch_case, entry) + { + hlsl_src_remove(&c->value); + hlsl_block_cleanup(&c->body); + list_remove(&c->entry); + vkd3d_free(c); + } + vkd3d_free(s); +} + static void free_ir_index(struct hlsl_ir_index *index) { hlsl_src_remove(&index->val); @@ -2934,6 +3042,10 @@ void hlsl_free_instr(struct hlsl_ir_node *node) case HLSL_IR_SWIZZLE: free_ir_swizzle(hlsl_ir_swizzle(node)); break; + + case HLSL_IR_SWITCH: + free_ir_switch(hlsl_ir_switch(node)); + break; } }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 44cebaaf6..f7ea0e4d3 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -284,6 +284,7 @@ enum hlsl_ir_node_type HLSL_IR_RESOURCE_STORE, HLSL_IR_STORE, HLSL_IR_SWIZZLE, + HLSL_IR_SWITCH, };
/* Common data for every type of IR instruction node. */ @@ -497,6 +498,20 @@ struct hlsl_ir_loop unsigned int next_index; /* liveness index of the end of the loop */ };
+struct hlsl_ir_switch_case +{ + struct hlsl_src value; + struct hlsl_block body; + struct list entry; +}; + +struct hlsl_ir_switch +{ + struct hlsl_ir_node node; + struct hlsl_src selector; + struct list cases; +}; + enum hlsl_ir_expr_op { HLSL_OP0_VOID, @@ -944,6 +959,12 @@ static inline struct hlsl_ir_index *hlsl_ir_index(const struct hlsl_ir_node *nod return CONTAINING_RECORD(node, struct hlsl_ir_index, node); }
+static inline struct hlsl_ir_switch *hlsl_ir_switch(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_SWITCH); + return CONTAINING_RECORD(node, struct hlsl_ir_switch, node); +} + static inline void hlsl_block_init(struct hlsl_block *block) { list_init(&block->instrs); @@ -1210,6 +1231,10 @@ struct hlsl_ir_node *hlsl_new_unary_expr(struct hlsl_ctx *ctx, enum hlsl_ir_expr struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct hlsl_type *type, const struct vkd3d_shader_location *loc, const struct hlsl_semantic *semantic, unsigned int modifiers, const struct hlsl_reg_reservation *reg_reservation); +struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, struct hlsl_ir_node *value, + struct hlsl_block *block); +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);
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.l b/libs/vkd3d-shader/hlsl.l index e9ae3ccf3..09815995d 100644 --- a/libs/vkd3d-shader/hlsl.l +++ b/libs/vkd3d-shader/hlsl.l @@ -46,7 +46,7 @@ static void update_location(struct hlsl_ctx *ctx, YYLTYPE *loc);
%x pp pp_line pp_pragma pp_ignore
-RESERVED1 auto|case|catch|char|class|const_cast|default|delete|dynamic_cast|enum +RESERVED1 auto|catch|char|class|const_cast|delete|dynamic_cast|enum RESERVED2 explicit|friend|goto|long|mutable|new|operator|private|protected|public RESERVED3 reinterpret_cast|short|signed|sizeof|static_cast|template|this|throw|try RESERVED4 typename|union|unsigned|using|virtual @@ -73,12 +73,14 @@ ANY (.) BlendState {return KW_BLENDSTATE; } break {return KW_BREAK; } Buffer {return KW_BUFFER; } +case {return KW_CASE; } cbuffer {return KW_CBUFFER; } compile {return KW_COMPILE; } const {return KW_CONST; } continue {return KW_CONTINUE; } DepthStencilState {return KW_DEPTHSTENCILSTATE; } DepthStencilView {return KW_DEPTHSTENCILVIEW; } +default {return KW_DEFAULT; } discard {return KW_DISCARD; } do {return KW_DO; } double {return KW_DOUBLE; } diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index ab9f81a38..44de54c5b 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -1134,6 +1134,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str case HLSL_IR_RESOURCE_LOAD: case HLSL_IR_RESOURCE_STORE: case HLSL_IR_STORE: + case HLSL_IR_SWITCH: hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Expected literal expression."); } @@ -4587,11 +4588,13 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type %token KW_BLENDSTATE %token KW_BREAK %token KW_BUFFER +%token KW_CASE %token KW_CBUFFER %token KW_COLUMN_MAJOR %token KW_COMPILE %token KW_CONST %token KW_CONTINUE +%token KW_DEFAULT %token KW_DEPTHSTENCILSTATE %token KW_DEPTHSTENCILVIEW %token KW_DISCARD @@ -4695,6 +4698,8 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type %type <list> type_specs %type <list> variables_def %type <list> variables_def_typed +%type <list> switch_case +%type <list> switch_cases
%token <name> VAR_IDENTIFIER %token <name> NEW_IDENTIFIER @@ -4737,6 +4742,7 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type %type <block> statement %type <block> statement_list %type <block> struct_declaration_without_vars +%type <block> switch_statement %type <block> unary_expr
%type <boolval> boolean @@ -6059,6 +6065,7 @@ statement: | jump_statement | selection_statement | loop_statement + | switch_statement
jump_statement: KW_BREAK ';' @@ -6181,6 +6188,52 @@ loop_statement: hlsl_pop_scope(ctx); }
+switch_statement: + attribute_list_optional KW_SWITCH '(' expr ')' '{' switch_cases '}' + { + struct hlsl_ir_node *selector = node_from_block($4); + struct hlsl_ir_node *s; + + if (!(selector = add_implicit_conversion(ctx, $4, selector, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), &@4))) + YYABORT; + + if (!(s = hlsl_new_switch(ctx, selector, $7, &@2))) + YYABORT; + vkd3d_free($7); + $$ = $4; + hlsl_block_add_instr($$, s); + } + +switch_case: + KW_CASE expr ':' statement_list + { + struct hlsl_ir_node *value = node_from_block($2); + hlsl_block_add_block($2, $4); + $$ = &hlsl_new_switch_case(ctx, value, $2)->entry; + } + | KW_CASE expr ':' + { + struct hlsl_ir_node *value = node_from_block($2); + $$ = &hlsl_new_switch_case(ctx, value, NULL)->entry; + } + | KW_DEFAULT ':' statement_list + { + $$ = &hlsl_new_switch_case(ctx, NULL, $3)->entry; + } + +switch_cases: + switch_case + { + if (!($$ = make_empty_list(ctx))) + YYABORT; + list_add_head($$, $1); + } + | switch_cases switch_case + { + $$ = $1; + list_add_tail($$, $2); + } + expr_optional: %empty { diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index be0248421..3a9765e5d 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -562,7 +562,19 @@ bool hlsl_transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, progress |= hlsl_transform_ir(ctx, func, &iff->else_block, context); } else if (instr->type == HLSL_IR_LOOP) + { progress |= hlsl_transform_ir(ctx, func, &hlsl_ir_loop(instr)->body, context); + } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + progress |= hlsl_transform_ir(ctx, func, &c->body, context); + } + }
progress |= func(ctx, instr, context); } @@ -822,6 +834,28 @@ static bool lower_return(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fun } } } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + has_early_return |= lower_return(ctx, func, &c->body, true); + } + + if (has_early_return) + { + /* If we're in a loop, we don't need to do anything here. We + * turned the return into a break, and that will already skip + * anything that comes after this "if" block. */ + if (!in_loop) + { + cf_instr = instr; + break; + } + } + } }
if (return_instr) @@ -1632,6 +1666,19 @@ static void copy_propagation_invalidate_from_block(struct hlsl_ctx *ctx, struct break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_invalidate_from_block(ctx, state, &c->body); + } + + break; + } + default: break; } @@ -1680,6 +1727,28 @@ static bool copy_propagation_process_loop(struct hlsl_ctx *ctx, struct hlsl_ir_l return progress; }
+static bool copy_propagation_process_switch(struct hlsl_ctx *ctx, struct hlsl_ir_switch *s, + struct copy_propagation_state *state) +{ + struct copy_propagation_state inner_state; + struct hlsl_ir_switch_case *c; + bool progress = false; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_state_init(ctx, &inner_state, state); + progress |= copy_propagation_transform_block(ctx, &c->body, &inner_state); + copy_propagation_state_destroy(&inner_state); + } + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_invalidate_from_block(ctx, state, &c->body); + } + + return progress; +} + static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_block *block, struct copy_propagation_state *state) { @@ -1718,7 +1787,15 @@ static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_b progress |= copy_propagation_process_loop(ctx, hlsl_ir_loop(instr), state); break;
- default: + case HLSL_IR_SWITCH: + progress |= copy_propagation_process_switch(ctx, hlsl_ir_switch(instr), state); + break; + + case HLSL_IR_CALL: + case HLSL_IR_CONSTANT: + case HLSL_IR_EXPR: + case HLSL_IR_INDEX: + case HLSL_IR_JUMP: break; } } @@ -2922,6 +2999,7 @@ static bool dce(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) case HLSL_IR_JUMP: case HLSL_IR_LOOP: case HLSL_IR_RESOURCE_STORE: + case HLSL_IR_SWITCH: break; }
@@ -2949,6 +3027,16 @@ static unsigned int index_instructions(struct hlsl_block *block, unsigned int in index = index_instructions(&hlsl_ir_loop(instr)->body, index); hlsl_ir_loop(instr)->next_index = index; } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + index = index_instructions(&c->body, index); + } + } }
return index; @@ -3158,6 +3246,20 @@ static void compute_liveness_recurse(struct hlsl_block *block, unsigned int loop jump->condition.node->last_read = last_read; break; } + case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + compute_liveness_recurse(&c->body, loop_first, loop_last); + if (c->value.node) + c->value.node->last_read = last_read; + } + s->selector.node->last_read = last_read; + break; + } case HLSL_IR_CONSTANT: break; } @@ -3508,6 +3610,18 @@ static void allocate_temp_registers_recurse(struct hlsl_ctx *ctx, break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + allocate_temp_registers_recurse(ctx, &c->body, allocator); + } + break; + } + default: break; } @@ -3617,6 +3731,18 @@ static void allocate_const_registers_recurse(struct hlsl_ctx *ctx, break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + allocate_const_registers_recurse(ctx, &c->body, allocator); + } + break; + } + default: break; } diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index 045fb6c5f..9a02ea45b 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -5430,6 +5430,45 @@ static void write_sm4_store(const struct tpf_writer *tpf, const struct hlsl_ir_s write_sm4_instruction(tpf, &instr); }
+static void write_sm4_switch(const struct tpf_writer *tpf, const struct hlsl_ir_switch *s) +{ + const struct hlsl_ir_node *selector = s->selector.node; + struct hlsl_ir_switch_case *c; + struct sm4_instruction instr; + + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM4_OP_SWITCH; + + sm4_src_from_node(&instr.srcs[0], selector, VKD3DSP_WRITEMASK_ALL); + instr.src_count = 1; + + write_sm4_instruction(tpf, &instr); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + memset(&instr, 0, sizeof(instr)); + if (c->value.node) + { + instr.opcode = VKD3D_SM4_OP_CASE; + + sm4_src_from_node(&instr.srcs[0], c->value.node, VKD3DSP_WRITEMASK_ALL); + instr.src_count = 1; + } + else + { + instr.opcode = VKD3D_SM4_OP_DEFAULT; + } + + write_sm4_instruction(tpf, &instr); + write_sm4_block(tpf, &c->body); + } + + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM4_OP_ENDSWITCH; + + write_sm4_instruction(tpf, &instr); +} + static void write_sm4_swizzle(const struct tpf_writer *tpf, const struct hlsl_ir_swizzle *swizzle) { struct sm4_instruction instr; @@ -5515,6 +5554,10 @@ static void write_sm4_block(const struct tpf_writer *tpf, const struct hlsl_bloc write_sm4_store(tpf, hlsl_ir_store(instr)); break;
+ case HLSL_IR_SWITCH: + write_sm4_switch(tpf, hlsl_ir_switch(instr)); + break; + case HLSL_IR_SWIZZLE: write_sm4_swizzle(tpf, hlsl_ir_swizzle(instr)); break; diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 9ccb917c8..ae0312d45 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -981,8 +981,7 @@ static int vkd3d_shader_scan_instruction(struct vkd3d_shader_scan_context *conte cf_info->type = VKD3D_SHADER_BLOCK_SWITCH; break; case VKD3DSIH_ENDSWITCH: - if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context)) - || cf_info->type != VKD3D_SHADER_BLOCK_SWITCH || cf_info->inside_block) + if (!(cf_info = vkd3d_shader_scan_get_current_cf_info(context)) || cf_info->type != VKD3D_SHADER_BLOCK_SWITCH) { vkd3d_shader_scan_error(context, VKD3D_SHADER_ERROR_TPF_MISMATCHED_CF, "Encountered ‘endswitch’ instruction without corresponding ‘switch’ block."); diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test new file mode 100644 index 000000000..799ff3fdd --- /dev/null +++ b/tests/hlsl/switch.shader_test @@ -0,0 +1,29 @@ +[require] +shader model >= 4.0 + +[pixel shader] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + default: + return 5.0; + } +} + +[test] +uniform 0 uint4 3 0 0 0 +draw quad +probe all rgba (5.0, 5.0, 5.0, 5.0) +uniform 0 uint4 1 0 0 0 +draw quad +probe all rgba (4.0, 4.0, 4.0, 4.0) +uniform 0 uint4 0 0 0 0 +draw quad +probe all rgba (3.0, 3.0, 3.0, 3.0)
You have already !245 open and waiting on your iteration, which is also about control flow and partially overlapping with this one. What's holding back that one? Could you please first finish that and then submit this one?
On Mon Sep 25 09:41:34 2023 +0000, Giovanni Mascellani wrote:
You have already !245 open and waiting on your iteration, which is also about control flow and partially overlapping with this one. What's holding back that one? Could you please first finish that and then submit this one?
We can mark !245 as draft if that helps. I don't think it's related at all or, or holding anything back.
On Mon Sep 25 09:41:34 2023 +0000, Nikolay Sivov wrote:
We can mark !245 as draft if that helps. I don't think it's related at all or, or holding anything back.
But why can't that this progress anyway? I'm not really thrilled to review MRs that are later abandoned, especially given that that one took some time and is rather close to be ready.
On Mon Sep 25 09:59:41 2023 +0000, Giovanni Mascellani wrote:
But why can't that this progress anyway? I'm not really thrilled to review MRs that are later abandoned, especially given that that one took some time and is rather close to be ready.
It can, and it will. It's not abandoned, and comments are still there. Is there anything I can do for this MR in your opinion?
Giovanni Mascellani (@giomasce) commented about tests/hlsl/switch.shader_test:
+[pixel shader] +uint4 v;
+float4 main() : sv_target +{
- switch (v.x)
- {
- case 0:
return 3.0;
- case 1:
return 4.0;
- default:
return 5.0;
- }
+}
There are probably a few other things we'd like to test: * falling through, together with its correct side effects; * `break` working properly; * `break` and `continue` doing the right thing for a `switch` inside a `for`; * check that non-constant cases are rejected; * check that cases which require constant folding work properly (i.e., `case 1+1:`); * check that duplicate cases are rejected; * maybe even checking a `switch` without a `default`.
On top of that, it would be nice to have the tests introduced at the beginning as `todo` and then solved as needed.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/tpf.c:
write_sm4_instruction(tpf, &instr);
}
+static void write_sm4_switch(const struct tpf_writer *tpf, const struct hlsl_ir_switch *s)
This commit is quite crowded already, please move TPF writing to a different one.
I haven't read in depth yet, but here are some preliminary comments.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/hlsl.h:
unsigned int next_index; /* liveness index of the end of the loop */
};
+struct hlsl_ir_switch_case +{
- struct hlsl_src value;
- struct hlsl_block body;
- struct list entry;
+};
+struct hlsl_ir_switch +{
- struct hlsl_ir_node node;
- struct hlsl_src selector;
- struct list cases;
+};
Another alternative would be to have something like that: ```c struct hlsl_ir_switch { struct hlsl_ir_node; struct hlsl_src selector; struct hlsl_block body; } ``` and then have `case` to be another instruction type. Right now I can't see any strong reasons to prefer one over the other, but maybe it wouldn't be bad to give it some thought. Have you already considered if one of those two alternatives would be easier to deal with than the other?
On Thu Oct 5 14:22:53 2023 +0000, Giovanni Mascellani wrote:
Another alternative would be to have something like that:
struct hlsl_ir_switch { struct hlsl_ir_node; struct hlsl_src selector; struct hlsl_block body; }
and then have `case` to be another instruction type. Right now I can't see any strong reasons to prefer one over the other, but maybe it wouldn't be bad to give it some thought. Have you already considered if one of those two alternatives would be easier to deal with than the other?
I did, I think. Thing is 'case' is not meaningful outside of switches, and is not something to support on its own when writing out instructions. The argument for it might be to avoid adding more types, and use what we already have - blocks. Another option might be to add IR_JUMP_CASE for it, and use hlsl_ir_jump.condition for hlsl_ir_switch_case.value.
On Thu Oct 5 14:17:41 2023 +0000, Giovanni Mascellani wrote:
There are probably a few other things we'd like to test:
- falling through, together with its correct side effects;
- `break` working properly;
- `break` and `continue` doing the right thing for a `switch` inside a `for`;
- check that non-constant cases are rejected;
- check that cases which require constant folding work properly (i.e.,
`case 1+1:`);
- check that duplicate cases are rejected;
- maybe even checking a `switch` without a `default`.
On top of that, it would be nice to have the tests introduced at the beginning as `todo` and then solved as needed.
Yes, it all makes sense, except for loops. I don't think it belongs here. It would be a test for loops.
On Thu Oct 5 15:35:08 2023 +0000, Nikolay Sivov wrote:
Yes, it all makes sense, except for loops. I don't think it belongs here. It would be a test for loops.
It's not very important to me where it is, it's ok if it's in another file.