From: Conor McCarthy cmccarthy@codeweavers.com
Ensures they are loaded only once per function independent of the control flow graph. --- libs/vkd3d-shader/spirv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index a25edb644..c884f23b1 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2370,6 +2370,7 @@ static bool is_in_fork_or_join_phase(const struct spirv_compiler *compiler) }
static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *compiler); +static size_t spirv_compiler_get_current_function_location(struct spirv_compiler *compiler);
static const char *spirv_compiler_get_entry_point_name(const struct spirv_compiler *compiler) { @@ -3444,11 +3445,13 @@ static uint32_t spirv_compiler_get_descriptor_index(struct spirv_compiler *compi index_ids[0] = compiler->descriptor_offsets_member_id; index_ids[1] = spirv_compiler_get_constant_uint(compiler, push_constant_index); ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPushConstant, type_id); + vkd3d_spirv_begin_function_stream_insertion(builder, + spirv_compiler_get_current_function_location(compiler)); ptr_id = vkd3d_spirv_build_op_in_bounds_access_chain(builder, ptr_type_id, compiler->push_constants_var_id, index_ids, 2); offset_id = vkd3d_spirv_build_op_load(builder, type_id, ptr_id, SpvMemoryAccessMaskNone); - if (!compiler->control_flow_depth) - compiler->descriptor_offset_ids[push_constant_index] = offset_id; + vkd3d_spirv_end_function_stream_insertion(builder); + compiler->descriptor_offset_ids[push_constant_index] = offset_id; } index_id = vkd3d_spirv_build_op_iadd(builder, type_id, index_id, offset_id); } @@ -6532,6 +6535,9 @@ static void spirv_compiler_emit_hull_shader_main(struct spirv_compiler *compiler struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; uint32_t void_id;
+ /* If a patch constant function used descriptor indexing the offsets must be reloaded. */ + memset(compiler->descriptor_offset_ids, 0, compiler->offset_info.descriptor_table_count + * sizeof(*compiler->descriptor_offset_ids)); vkd3d_spirv_builder_begin_main_function(builder);
void_id = vkd3d_spirv_get_op_type_void(builder);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 2 +- libs/vkd3d-shader/tpf.c | 2 +- libs/vkd3d-shader/vkd3d_shader_main.c | 18 +++++++++--------- libs/vkd3d-shader/vkd3d_shader_private.h | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index e0e242cb7..a59692dcc 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -2307,7 +2307,7 @@ static enum vkd3d_result register_allocate_constant_array(struct vkd3d_shader_re "Out of memory allocating an immediate constant buffer of count %u.", count); return VKD3D_ERROR_OUT_OF_MEMORY; } - if ((reg->idx[0].offset = shader_instruction_array_add_icb(&sm6->p.instructions, icb)) == UINT_MAX) + if ((reg->idx[0].offset = shader_instruction_array_add_opaque_param(&sm6->p.instructions, icb)) == UINT_MAX) { ERR("Failed to store icb object.\n"); vkd3d_free(icb); diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index fbc04f61f..40a2f3e44 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -790,7 +790,7 @@ static void shader_sm4_read_shader_data(struct vkd3d_shader_instruction *ins, ui icb->component_count = VKD3D_VEC4_SIZE; icb->element_count = icb_size / VKD3D_VEC4_SIZE; memcpy(icb->data, tokens, sizeof(*tokens) * icb_size); - shader_instruction_array_add_icb(&priv->p.instructions, icb); + shader_instruction_array_add_opaque_param(&priv->p.instructions, icb); ins->declaration.icb = icb; }
diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 28d647b3d..55940da8a 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -1864,14 +1864,14 @@ bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *ins return true; }
-unsigned int shader_instruction_array_add_icb(struct vkd3d_shader_instruction_array *instructions, - struct vkd3d_shader_immediate_constant_buffer *icb) +unsigned int shader_instruction_array_add_opaque_param(struct vkd3d_shader_instruction_array *instructions, + void *param) { - if (!vkd3d_array_reserve((void **)&instructions->icbs, &instructions->icb_capacity, instructions->icb_count + 1, - sizeof(*instructions->icbs))) + if (!vkd3d_array_reserve((void **)&instructions->opaque_params, &instructions->opaque_param_capacity, + instructions->opaque_param_count + 1, sizeof(*instructions->opaque_params))) return UINT_MAX; - instructions->icbs[instructions->icb_count] = icb; - return instructions->icb_count++; + instructions->opaque_params[instructions->opaque_param_count] = param; + return instructions->opaque_param_count++; }
static struct vkd3d_shader_src_param *shader_instruction_array_clone_src_params( @@ -1959,9 +1959,9 @@ void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *ins vkd3d_free(instructions->elements); shader_param_allocator_destroy(&instructions->dst_params); shader_param_allocator_destroy(&instructions->src_params); - for (i = 0; i < instructions->icb_count; ++i) - vkd3d_free(instructions->icbs[i]); - vkd3d_free(instructions->icbs); + for (i = 0; i < instructions->opaque_param_count; ++i) + vkd3d_free(instructions->opaque_params[i]); + vkd3d_free(instructions->opaque_params); }
void vkd3d_shader_build_varying_map(const struct vkd3d_shader_signature *output_signature, diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index ab7300115..92bdd9661 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1179,15 +1179,15 @@ struct vkd3d_shader_instruction_array
struct vkd3d_shader_param_allocator src_params; struct vkd3d_shader_param_allocator dst_params; - struct vkd3d_shader_immediate_constant_buffer **icbs; - size_t icb_capacity; - size_t icb_count; + void **opaque_params; + size_t opaque_param_capacity; + size_t opaque_param_count; };
bool shader_instruction_array_init(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); -unsigned int shader_instruction_array_add_icb(struct vkd3d_shader_instruction_array *instructions, - struct vkd3d_shader_immediate_constant_buffer *icb); +unsigned int shader_instruction_array_add_opaque_param(struct vkd3d_shader_instruction_array *instructions, + void *param); bool shader_instruction_array_clone_instruction(struct vkd3d_shader_instruction_array *instructions, unsigned int dst, unsigned int src); void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *instructions);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 41 +- libs/vkd3d-shader/ir.c | 540 +++++++++++++++++++++++ libs/vkd3d-shader/spirv.c | 540 +++++------------------ libs/vkd3d-shader/vkd3d_shader_private.h | 28 ++ 4 files changed, 719 insertions(+), 430 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 5cea0c0c2..2194e3ca2 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -44,6 +44,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_BEM ] = "bem", [VKD3DSIH_BFI ] = "bfi", [VKD3DSIH_BFREV ] = "bfrev", + [VKD3DSIH_BRANCH ] = "branch", [VKD3DSIH_BREAK ] = "break", [VKD3DSIH_BREAKC ] = "breakc", [VKD3DSIH_BREAKP ] = "breakp", @@ -1516,6 +1517,7 @@ static void shader_dump_instruction_flags(struct vkd3d_d3d_asm_compiler *compile
switch (ins->handler_idx) { + case VKD3DSIH_BRANCH: case VKD3DSIH_BREAKP: case VKD3DSIH_CONTINUEP: case VKD3DSIH_DISCARD: @@ -1610,6 +1612,27 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler,
switch (ins->handler_idx) { + case VKD3DSIH_BRANCH: + { + const struct vkd3d_shader_branch *branch = &ins->declaration.branch; + if (branch->condition.reg.type != VKD3DSPR_INVALID) + { + shader_dump_instruction_flags(compiler, ins); + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_src_param(compiler, &branch->condition); + vkd3d_string_buffer_printf(buffer, " ? %u : %u", branch->true_id, branch->false_id); + } + else + { + vkd3d_string_buffer_printf(buffer, " %u", branch->true_id); + } + if (branch->merge_block_id) + vkd3d_string_buffer_printf(buffer, ", merge %u", branch->merge_block_id); + if (branch->continue_block_id) + vkd3d_string_buffer_printf(buffer, ", continue %u", branch->continue_block_id); + break; + } + case VKD3DSIH_DCL: case VKD3DSIH_DCL_UAV_TYPED: vkd3d_string_buffer_printf(buffer, "%s", compiler->colours.opcode); @@ -1825,6 +1848,20 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, shader_print_bool_literal(compiler, " = ", ins->src[0].reg.u.immconst_uint[0], ""); break;
+ case VKD3DSIH_SWITCH: + if (ins->declaration.switch_.case_count) + { + const struct vkd3d_shader_switch *switch_ = &ins->declaration.switch_; + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_src_param(compiler, &switch_->src); + for (i = 0; i < switch_->case_count; ++i) + { + vkd3d_string_buffer_printf(buffer, ", %u: %u", switch_->cases[i].value, switch_->cases[i].block_id); + } + vkd3d_string_buffer_printf(buffer, ", default: %u", switch_->default_id); + break; + } + /* fall through */ default: shader_dump_instruction_flags(compiler, ins);
@@ -1972,9 +2009,11 @@ enum vkd3d_result vkd3d_dxbc_binary_to_text(const struct vkd3d_shader_instructio case VKD3DSIH_ELSE: case VKD3DSIH_IF: case VKD3DSIH_LOOP: - case VKD3DSIH_SWITCH: ++indent; break; + case VKD3DSIH_SWITCH: + indent += !ins->declaration.switch_.case_count; + break;
default: break; diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 758b594b3..7c0bf5dd1 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1429,6 +1429,527 @@ static enum vkd3d_result normalise_combined_samplers(struct vkd3d_shader_parser return VKD3D_OK; }
+struct vkd3d_if_cf_info +{ + size_t ins_location; + unsigned int id; + unsigned int else_block_id; +}; + +struct vkd3d_loop_cf_info +{ + unsigned int header_block_id; + unsigned int continue_block_id; +}; + +struct vkd3d_switch_cf_info +{ + size_t ins_location; + unsigned int id; + unsigned int default_block_id; + struct vkd3d_shader_switch_case *cases; + size_t cases_size; + unsigned int cases_count; +}; + +struct vkd3d_control_flow_info +{ + union + { + struct vkd3d_if_cf_info if_; + struct vkd3d_loop_cf_info loop; + struct vkd3d_switch_cf_info switch_; + } u; + unsigned int merge_block_id; + + enum + { + VKD3D_BLOCK_IF, + VKD3D_BLOCK_LOOP, + VKD3D_BLOCK_SWITCH, + } current_block; + bool inside_block; +}; + +struct cf_flattener +{ + struct vkd3d_shader_parser *parser; + + struct vkd3d_shader_location location; + + struct vkd3d_shader_instruction *instructions; + size_t instruction_capacity; + size_t instruction_count; + + unsigned int block_id; + + unsigned int branch_id; + unsigned int loop_id; + unsigned int switch_id; + unsigned int control_flow_depth; + struct vkd3d_control_flow_info *control_flow_info; + size_t control_flow_info_size; +}; + +static struct vkd3d_shader_instruction *cf_flattener_require_space(struct cf_flattener *flattener, size_t count) +{ + if (!vkd3d_array_reserve((void **)&flattener->instructions, &flattener->instruction_capacity, + flattener->instruction_count + count, sizeof(*flattener->instructions))) + { + ERR("Failed to allocate instructions.\n"); + return NULL; + } + return &flattener->instructions[flattener->instruction_count]; +} + +static unsigned int cf_flattener_alloc_block_id(struct cf_flattener *flattener) +{ + return ++flattener->block_id; +} + +static void cf_flattener_emit_label(struct cf_flattener *flattener, unsigned int label_id) +{ + struct vkd3d_shader_src_param *src_param; + struct vkd3d_shader_instruction *ins; + + if (!(ins = cf_flattener_require_space(flattener, 1))) + return; + if (!(src_param = shader_parser_get_src_params(flattener->parser, 1))) + return; + + vsir_instruction_init(ins, &flattener->location, VKD3DSIH_LABEL); + ins->src = src_param; + ins->src_count = 1; + + vsir_register_init(&src_param->reg, VKD3DSPR_IMMCONST, VKD3D_DATA_UINT, 0); + src_param->reg.u.immconst_uint[0] = label_id; + src_param->swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); + src_param->modifiers = 0; + + ++flattener->instruction_count; +} + +static void cf_flattener_emit_conditional_branch_and_label(struct cf_flattener *flattener, + const struct vkd3d_shader_src_param *condition, unsigned int true_id, unsigned int flags) +{ + struct vkd3d_shader_instruction *ins; + unsigned int merge_block_id; + + if (!(ins = cf_flattener_require_space(flattener, 1))) + return; + vsir_instruction_init(ins, &flattener->location, VKD3DSIH_BRANCH); + merge_block_id = cf_flattener_alloc_block_id(flattener); + ins->declaration.branch.merge_block_id = merge_block_id; + ins->declaration.branch.continue_block_id = 0; + ins->declaration.branch.condition = *condition; + ins->declaration.branch.true_id = true_id; + ins->declaration.branch.false_id = merge_block_id; + ins->flags = flags; + ++flattener->instruction_count; + + cf_flattener_emit_label(flattener, merge_block_id); +} + +static void cf_flattener_emit_unconditional_branch(struct cf_flattener *flattener, unsigned int target_block_id) +{ + struct vkd3d_shader_instruction *ins; + + if (!(ins = cf_flattener_require_space(flattener, 1))) + return; + vsir_instruction_init(ins, &flattener->location, VKD3DSIH_BRANCH); + ins->declaration.branch.merge_block_id = 0; + ins->declaration.branch.continue_block_id = 0; + ins->declaration.branch.condition.reg.type = VKD3DSPR_INVALID; + ins->declaration.branch.true_id = target_block_id; + ins->declaration.branch.false_id = 0; + ++flattener->instruction_count; +} + +static struct vkd3d_control_flow_info *cf_flattener_push_control_flow_level( + struct cf_flattener *flattener) +{ + if (!vkd3d_array_reserve((void **)&flattener->control_flow_info, &flattener->control_flow_info_size, + flattener->control_flow_depth + 1, sizeof(*flattener->control_flow_info))) + { + ERR("Failed to allocate control flow info structure.\n"); + return NULL; + } + + return &flattener->control_flow_info[flattener->control_flow_depth++]; +} + +static void cf_flattener_pop_control_flow_level(struct cf_flattener *flattener) +{ + struct vkd3d_control_flow_info *cf_info; + + assert(flattener->control_flow_depth); + + cf_info = &flattener->control_flow_info[--flattener->control_flow_depth]; + memset(cf_info, 0, sizeof(*cf_info)); +} + +static struct vkd3d_control_flow_info *cf_flattener_find_innermost_loop( + struct cf_flattener *flattener) +{ + int depth; + + for (depth = flattener->control_flow_depth - 1; depth >= 0; --depth) + { + if (flattener->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP) + return &flattener->control_flow_info[depth]; + } + + return NULL; +} + +static struct vkd3d_control_flow_info *cf_flattener_find_innermost_breakable_cf_construct( + struct cf_flattener *flattener) +{ + int depth; + + for (depth = flattener->control_flow_depth - 1; depth >= 0; --depth) + { + if (flattener->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP + || flattener->control_flow_info[depth].current_block == VKD3D_BLOCK_SWITCH) + return &flattener->control_flow_info[depth]; + } + + return NULL; +} + +static enum vkd3d_result flatten_control_flow_constructs(struct vkd3d_shader_parser *parser, + struct cf_flattener *flattener) +{ + struct vkd3d_shader_instruction_array *instructions = &parser->instructions; + size_t i; + + flattener->parser = parser; + + if (!cf_flattener_require_space(flattener, instructions->count)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < instructions->count; ++i) + { + unsigned int loop_header_block_id, loop_body_block_id, continue_block_id, merge_block_id, true_block_id; + const struct vkd3d_shader_instruction *instruction = &instructions->elements[i]; + const struct vkd3d_shader_src_param *src = instruction->src; + struct vkd3d_shader_instruction *dst_ins; + struct vkd3d_control_flow_info *cf_info; + + flattener->location = instruction->location; + + cf_info = flattener->control_flow_depth + ? &flattener->control_flow_info[flattener->control_flow_depth - 1] : NULL; + + switch (instruction->handler_idx) + { + case VKD3DSIH_IF: + if (!(cf_info = cf_flattener_push_control_flow_level(flattener))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + true_block_id = cf_flattener_alloc_block_id(flattener); + merge_block_id = cf_flattener_alloc_block_id(flattener); + + cf_info->u.if_.ins_location = flattener->instruction_count; + + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + vsir_instruction_init(dst_ins, &instruction->location, VKD3DSIH_BRANCH); + dst_ins->declaration.branch.merge_block_id = merge_block_id; + dst_ins->declaration.branch.continue_block_id = 0; + dst_ins->declaration.branch.condition = *src; + dst_ins->declaration.branch.true_id = true_block_id; + dst_ins->declaration.branch.false_id = merge_block_id; + ++flattener->instruction_count; + + cf_flattener_emit_label(flattener, true_block_id); + + cf_info->u.if_.id = flattener->branch_id; + cf_info->merge_block_id = merge_block_id; + cf_info->u.if_.else_block_id = 0; + cf_info->inside_block = true; + cf_info->current_block = VKD3D_BLOCK_IF; + + ++flattener->branch_id; + break; + + case VKD3DSIH_ELSE: + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_IF); + + cf_info->u.if_.else_block_id = cf_flattener_alloc_block_id(flattener); + + if (cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, cf_info->merge_block_id); + + dst_ins = &flattener->instructions[cf_info->u.if_.ins_location]; + dst_ins->declaration.branch.false_id = cf_info->u.if_.else_block_id; + + cf_flattener_emit_label(flattener, cf_info->u.if_.else_block_id); + + cf_info->inside_block = true; + break; + + case VKD3DSIH_ENDIF: + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_IF); + + if (cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, cf_info->merge_block_id); + + cf_flattener_emit_label(flattener, cf_info->merge_block_id); + + cf_flattener_pop_control_flow_level(flattener); + break; + + case VKD3DSIH_LOOP: + if (!(cf_info = cf_flattener_push_control_flow_level(flattener))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + loop_header_block_id = cf_flattener_alloc_block_id(flattener); + loop_body_block_id = cf_flattener_alloc_block_id(flattener); + continue_block_id = cf_flattener_alloc_block_id(flattener); + merge_block_id = cf_flattener_alloc_block_id(flattener); + + cf_flattener_emit_unconditional_branch(flattener, loop_header_block_id); + cf_flattener_emit_label(flattener, loop_header_block_id); + + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + vsir_instruction_init(dst_ins, &flattener->location, VKD3DSIH_BRANCH); + dst_ins->declaration.branch.merge_block_id = merge_block_id; + dst_ins->declaration.branch.continue_block_id = continue_block_id; + dst_ins->declaration.branch.condition.reg.type = VKD3DSPR_INVALID; + dst_ins->declaration.branch.true_id = loop_body_block_id; + dst_ins->declaration.branch.false_id = 0; + ++flattener->instruction_count; + cf_flattener_emit_label(flattener, loop_body_block_id); + + cf_info->u.loop.header_block_id = loop_header_block_id; + cf_info->u.loop.continue_block_id = continue_block_id; + cf_info->merge_block_id = merge_block_id; + cf_info->current_block = VKD3D_BLOCK_LOOP; + cf_info->inside_block = true; + + ++flattener->loop_id; + break; + + case VKD3DSIH_ENDLOOP: + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_LOOP); + + if (cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, cf_info->u.loop.continue_block_id); + + cf_flattener_emit_label(flattener, cf_info->u.loop.continue_block_id); + cf_flattener_emit_unconditional_branch(flattener, cf_info->u.loop.header_block_id); + cf_flattener_emit_label(flattener, cf_info->merge_block_id); + + cf_flattener_pop_control_flow_level(flattener); + break; + + case VKD3DSIH_SWITCH: + if (!(cf_info = cf_flattener_push_control_flow_level(flattener))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + merge_block_id = cf_flattener_alloc_block_id(flattener); + + assert(src->reg.data_type == VKD3D_DATA_INT); + + cf_info->u.if_.ins_location = flattener->instruction_count; + + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + vsir_instruction_init(dst_ins, &instruction->location, VKD3DSIH_SWITCH); + dst_ins->declaration.switch_.merge_block_id = merge_block_id; + dst_ins->declaration.switch_.src = *src; + dst_ins->declaration.switch_.cases = NULL; + dst_ins->declaration.switch_.case_count = 0; + dst_ins->declaration.switch_.default_id = 0; + ++flattener->instruction_count; + + cf_info->u.switch_.id = flattener->switch_id; + cf_info->merge_block_id = merge_block_id; + cf_info->u.switch_.cases = NULL; + cf_info->u.switch_.cases_size = 0; + cf_info->u.switch_.cases_count = 0; + cf_info->u.switch_.default_block_id = 0; + cf_info->inside_block = false; + cf_info->current_block = VKD3D_BLOCK_SWITCH; + + ++flattener->switch_id; + + if (!vkd3d_array_reserve((void **)&cf_info->u.switch_.cases, &cf_info->u.switch_.cases_size, + 10, sizeof(*cf_info->u.switch_.cases))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + break; + + case VKD3DSIH_ENDSWITCH: + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); + assert(!cf_info->inside_block); + + if (!cf_info->u.switch_.default_block_id) + cf_info->u.switch_.default_block_id = cf_info->merge_block_id; + + cf_flattener_emit_label(flattener, cf_info->merge_block_id); + + /* The SWITCH instruction is completed when the endswitch + * instruction is processed because we do not know the number + * of case statements or the default block id in advance.*/ + dst_ins = &flattener->instructions[cf_info->u.switch_.ins_location]; + dst_ins->declaration.switch_.cases = cf_info->u.switch_.cases; + dst_ins->declaration.switch_.case_count = cf_info->u.switch_.cases_count; + dst_ins->declaration.switch_.default_id = cf_info->u.switch_.default_block_id; + shader_instruction_array_add_opaque_param(instructions, cf_info->u.switch_.cases); + + cf_flattener_pop_control_flow_level(flattener); + break; + + case VKD3DSIH_CASE: + { + unsigned int label_id, value; + + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); + + assert(src->reg.type == VKD3DSPR_IMMCONST); + value = *src->reg.u.immconst_uint; + + if (!vkd3d_array_reserve((void **)&cf_info->u.switch_.cases, &cf_info->u.switch_.cases_size, + cf_info->u.switch_.cases_count + 1, sizeof(*cf_info->u.switch_.cases))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + label_id = cf_flattener_alloc_block_id(flattener); + if (cf_info->inside_block) /* fall-through */ + cf_flattener_emit_unconditional_branch(flattener, label_id); + + cf_info->u.switch_.cases[cf_info->u.switch_.cases_count].value = value; + cf_info->u.switch_.cases[cf_info->u.switch_.cases_count].block_id = label_id; + ++cf_info->u.switch_.cases_count; + + cf_flattener_emit_label(flattener, label_id); + + cf_info->inside_block = true; + break; + } + + case VKD3DSIH_DEFAULT: + assert(flattener->control_flow_depth); + assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); + assert(!cf_info->u.switch_.default_block_id); + + cf_info->u.switch_.default_block_id = cf_flattener_alloc_block_id(flattener); + if (cf_info->inside_block) /* fall-through */ + cf_flattener_emit_unconditional_branch(flattener, cf_info->u.switch_.default_block_id); + + cf_flattener_emit_label(flattener, cf_info->u.switch_.default_block_id); + + cf_info->inside_block = true; + break; + + case VKD3DSIH_BREAK: + { + struct vkd3d_control_flow_info *breakable_cf_info; + + assert(flattener->control_flow_depth); + + if (!(breakable_cf_info = cf_flattener_find_innermost_breakable_cf_construct(flattener))) + { + FIXME("Unhandled break instruction.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (breakable_cf_info->current_block == VKD3D_BLOCK_LOOP) + { + cf_flattener_emit_unconditional_branch(flattener, breakable_cf_info->merge_block_id); + } + else if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) + { + /* The current case block may have already been ended by an + * unconditional continue instruction. */ + if (breakable_cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, breakable_cf_info->merge_block_id); + } + + cf_info->inside_block = false; + break; + } + + case VKD3DSIH_BREAKP: + { + struct vkd3d_control_flow_info *loop_cf_info; + + assert(flattener->control_flow_depth); + + if (!(loop_cf_info = cf_flattener_find_innermost_loop(flattener))) + { + ERR("Invalid 'breakc' instruction outside loop.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + cf_flattener_emit_conditional_branch_and_label(flattener, src, loop_cf_info->merge_block_id, + instruction->flags); + break; + } + + case VKD3DSIH_CONTINUE: + { + struct vkd3d_control_flow_info *loop_cf_info; + + assert(flattener->control_flow_depth); + + if (!(loop_cf_info = cf_flattener_find_innermost_loop(flattener))) + { + ERR("Invalid 'continue' instruction outside loop.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + cf_flattener_emit_unconditional_branch(flattener, loop_cf_info->u.loop.continue_block_id); + + cf_info->inside_block = false; + break; + } + + case VKD3DSIH_CONTINUEP: + { + struct vkd3d_control_flow_info *loop_cf_info; + + if (!(loop_cf_info = cf_flattener_find_innermost_loop(flattener))) + { + ERR("Invalid 'continuec' instruction outside loop.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + cf_flattener_emit_conditional_branch_and_label(flattener, instruction->src, + loop_cf_info->u.loop.continue_block_id, instruction->flags); + break; + } + + case VKD3DSIH_RET: + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + *dst_ins = *instruction; + ++flattener->instruction_count; + + if (cf_info) + cf_info->inside_block = false; + break; + + default: + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + *dst_ins = *instruction; + ++flattener->instruction_count; + break; + } + } + + return VKD3D_OK; +} + enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, const struct vkd3d_shader_compile_info *compile_info) { @@ -1457,6 +1978,25 @@ enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, if (result >= 0) remove_dead_code(parser);
+ if (result >= 0) + { + struct cf_flattener flattener = {0}; + result = flatten_control_flow_constructs(parser, &flattener); + vkd3d_free(flattener.control_flow_info); + if (result >= 0) + { + vkd3d_free(parser->instructions.elements); + parser->instructions.elements = flattener.instructions; + parser->instructions.capacity = flattener.instruction_capacity; + parser->instructions.count = flattener.instruction_count; + parser->shader_desc.block_count = flattener.block_id; + } + else + { + vkd3d_free(flattener.instructions); + } + } + if (result >= 0) result = normalise_combined_samplers(parser);
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index c884f23b1..96dc3113f 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -348,6 +348,12 @@ static bool vkd3d_spirv_stream_append(struct vkd3d_spirv_stream *dst_stream, return true; }
+static void vkd3d_spirv_stream_add_word_count(struct vkd3d_spirv_stream *stream, size_t location, + unsigned int word_count) +{ + stream->words[location] += word_count << SpvWordCountShift; +} + struct vkd3d_spirv_builder { uint64_t capability_mask; @@ -851,20 +857,6 @@ static void vkd3d_spirv_end_function_stream_insertion(struct vkd3d_spirv_builder builder->insertion_location = ~(size_t)0; }
-struct vkd3d_spirv_op_branch_conditional -{ - uint32_t opcode; - uint32_t condition_id; - uint32_t true_label; - uint32_t false_label; -}; - -static struct vkd3d_spirv_op_branch_conditional *vkd3d_spirv_as_op_branch_conditional( - struct vkd3d_spirv_stream *stream, size_t location) -{ - return (struct vkd3d_spirv_op_branch_conditional *)&stream->words[location]; -} - static void vkd3d_spirv_build_op_capability(struct vkd3d_spirv_stream *stream, SpvCapability cap) { @@ -2206,51 +2198,6 @@ static const char *debug_vkd3d_symbol(const struct vkd3d_symbol *symbol) } }
-struct vkd3d_if_cf_info -{ - size_t stream_location; - unsigned int id; - uint32_t merge_block_id; - uint32_t else_block_id; -}; - -struct vkd3d_loop_cf_info -{ - uint32_t header_block_id; - uint32_t continue_block_id; - uint32_t merge_block_id; -}; - -struct vkd3d_switch_cf_info -{ - size_t stream_location; - unsigned int id; - uint32_t selector_id; - uint32_t merge_block_id; - uint32_t default_block_id; - uint32_t *case_blocks; - size_t case_blocks_size; - unsigned int case_block_count; -}; - -struct vkd3d_control_flow_info -{ - union - { - struct vkd3d_if_cf_info if_; - struct vkd3d_loop_cf_info loop; - struct vkd3d_switch_cf_info switch_; - } u; - - enum - { - VKD3D_BLOCK_IF, - VKD3D_BLOCK_LOOP, - VKD3D_BLOCK_SWITCH, - } current_block; - bool inside_block; -}; - struct vkd3d_push_constant_buffer_binding { struct vkd3d_shader_register reg; @@ -2297,13 +2244,6 @@ struct spirv_compiler
enum vkd3d_shader_type shader_type;
- unsigned int branch_id; - unsigned int loop_id; - unsigned int switch_id; - unsigned int control_flow_depth; - struct vkd3d_control_flow_info *control_flow_info; - size_t control_flow_info_size; - struct vkd3d_shader_interface_info shader_interface; struct vkd3d_shader_descriptor_offset_info offset_info; uint32_t descriptor_offsets_member_id; @@ -2352,6 +2292,9 @@ struct spirv_compiler
uint32_t *ssa_register_ids; unsigned int ssa_register_count; + + uint32_t *block_label_ids; + unsigned int block_count; };
static bool is_in_default_phase(const struct spirv_compiler *compiler) @@ -2381,8 +2324,6 @@ static const char *spirv_compiler_get_entry_point_name(const struct spirv_compil
static void spirv_compiler_destroy(struct spirv_compiler *compiler) { - vkd3d_free(compiler->control_flow_info); - vkd3d_free(compiler->output_info);
vkd3d_free(compiler->push_constants); @@ -2720,6 +2661,15 @@ static struct vkd3d_string_buffer *vkd3d_shader_register_range_string(struct spi return buffer; }
+static uint32_t spirv_compiler_get_label_id(struct spirv_compiler *compiler, unsigned int block_id) +{ + assert(block_id); + --block_id; + if (!compiler->block_label_ids[block_id]) + compiler->block_label_ids[block_id] = vkd3d_spirv_alloc_id(&compiler->spirv_builder); + return compiler->block_label_ids[block_id]; +} + static struct vkd3d_shader_descriptor_binding spirv_compiler_get_descriptor_binding( struct spirv_compiler *compiler, const struct vkd3d_shader_register *reg, const struct vkd3d_shader_register_range *range, enum vkd3d_shader_resource_type resource_type, @@ -6401,6 +6351,12 @@ static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler,
if (instruction->handler_idx == VKD3DSIH_HS_CONTROL_POINT_PHASE) compiler->emit_default_control_point_phase = instruction->flags; + + if (compiler->block_count) + { + memset(compiler->block_label_ids, 0, compiler->block_count * sizeof(*compiler->block_label_ids)); + spirv_compiler_get_label_id(compiler, 1); + } }
static void spirv_compiler_emit_default_control_point_phase(struct spirv_compiler *compiler) @@ -7363,379 +7319,101 @@ static void spirv_compiler_emit_kill(struct spirv_compiler *compiler, vkd3d_spirv_build_op_label(builder, merge_block_id); }
-static struct vkd3d_control_flow_info *spirv_compiler_push_control_flow_level( - struct spirv_compiler *compiler) +static bool spirv_compiler_init_blocks(struct spirv_compiler *compiler, unsigned int block_count) { - if (!vkd3d_array_reserve((void **)&compiler->control_flow_info, &compiler->control_flow_info_size, - compiler->control_flow_depth + 1, sizeof(*compiler->control_flow_info))) - { - ERR("Failed to allocate control flow info structure.\n"); - return NULL; - } + compiler->block_count = block_count; + + if (!(compiler->block_label_ids = vkd3d_calloc(block_count, sizeof(*compiler->block_label_ids)))) + return false;
- return &compiler->control_flow_info[compiler->control_flow_depth++]; + spirv_compiler_get_label_id(compiler, 1); + + return true; }
-static void spirv_compiler_pop_control_flow_level(struct spirv_compiler *compiler) +static void spirv_compiler_emit_label(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) { - struct vkd3d_control_flow_info *cf_info; - - assert(compiler->control_flow_depth); + const struct vkd3d_shader_src_param *src = instruction->src; + uint32_t label_id;
- cf_info = &compiler->control_flow_info[--compiler->control_flow_depth]; - memset(cf_info, 0, sizeof(*cf_info)); + assert(src->reg.type == VKD3DSPR_IMMCONST && src->reg.data_type == VKD3D_DATA_UINT); + label_id = spirv_compiler_get_label_id(compiler, src->reg.u.immconst_uint[0]); + vkd3d_spirv_build_op_label(&compiler->spirv_builder, label_id); }
-static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_loop( - struct spirv_compiler *compiler) +static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, + uint32_t merge_block_id, uint32_t continue_block_id) { - int depth; + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + + if (!merge_block_id) + return;
- for (depth = compiler->control_flow_depth - 1; depth >= 0; --depth) + merge_block_id = spirv_compiler_get_label_id(compiler, merge_block_id); + if (!continue_block_id) { - if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP) - return &compiler->control_flow_info[depth]; + vkd3d_spirv_build_op_selection_merge(builder, merge_block_id, SpvSelectionControlMaskNone); + } + else + { + continue_block_id = spirv_compiler_get_label_id(compiler, continue_block_id); + vkd3d_spirv_build_op_loop_merge(builder, merge_block_id, continue_block_id, SpvLoopControlMaskNone); } - - return NULL; }
-static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_cf_construct( - struct spirv_compiler *compiler) +static void spirv_compiler_emit_branch(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) { - int depth; + const struct vkd3d_shader_branch *branch = &instruction->declaration.branch; + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
- for (depth = compiler->control_flow_depth - 1; depth >= 0; --depth) + if (branch->condition.reg.type != VKD3DSPR_INVALID) { - if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP - || compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_SWITCH) - return &compiler->control_flow_info[depth]; + uint32_t condition_id = spirv_compiler_emit_load_src(compiler, &branch->condition, VKD3DSP_WRITEMASK_0); + condition_id = spirv_compiler_emit_int_to_bool(compiler, instruction->flags, 1, condition_id); + spirv_compiler_emit_merge(compiler, branch->merge_block_id, branch->continue_block_id); + vkd3d_spirv_build_op_branch_conditional(builder, condition_id, + spirv_compiler_get_label_id(compiler, branch->true_id), + spirv_compiler_get_label_id(compiler, branch->false_id)); + } + else + { + spirv_compiler_emit_merge(compiler, branch->merge_block_id, branch->continue_block_id); + vkd3d_spirv_build_op_branch(builder, spirv_compiler_get_label_id(compiler, branch->true_id)); } - - return NULL; }
-static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *compiler, +static void spirv_compiler_emit_switch(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - uint32_t loop_header_block_id, loop_body_block_id, continue_block_id; + const struct vkd3d_shader_switch *switch_ = &instruction->declaration.switch_; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - const struct vkd3d_shader_src_param *src = instruction->src; - uint32_t merge_block_id, val_id, condition_id, true_label; - struct vkd3d_control_flow_info *cf_info; - - cf_info = compiler->control_flow_depth - ? &compiler->control_flow_info[compiler->control_flow_depth - 1] : NULL; + struct vkd3d_spirv_stream *stream = &builder->function_stream; + const struct vkd3d_shader_switch_case *switch_case; + uint32_t val_id; + size_t location; + unsigned int i;
- switch (instruction->handler_idx) + if (switch_->src.swizzle != VKD3D_SHADER_SWIZZLE(X, X, X, X)) { - case VKD3DSIH_IF: - if (!(cf_info = spirv_compiler_push_control_flow_level(compiler))) - return VKD3D_ERROR_OUT_OF_MEMORY; - - val_id = spirv_compiler_emit_load_src(compiler, src, VKD3DSP_WRITEMASK_0); - condition_id = spirv_compiler_emit_int_to_bool(compiler, instruction->flags, 1, val_id); - - true_label = vkd3d_spirv_alloc_id(builder); - merge_block_id = vkd3d_spirv_alloc_id(builder); - vkd3d_spirv_build_op_selection_merge(builder, merge_block_id, SpvSelectionControlMaskNone); - cf_info->u.if_.stream_location = vkd3d_spirv_stream_current_location(&builder->function_stream); - vkd3d_spirv_build_op_branch_conditional(builder, condition_id, true_label, merge_block_id); - - vkd3d_spirv_build_op_label(builder, true_label); - - cf_info->u.if_.id = compiler->branch_id; - cf_info->u.if_.merge_block_id = merge_block_id; - cf_info->u.if_.else_block_id = 0; - cf_info->inside_block = true; - cf_info->current_block = VKD3D_BLOCK_IF; - - vkd3d_spirv_build_op_name(builder, merge_block_id, "branch%u_merge", compiler->branch_id); - vkd3d_spirv_build_op_name(builder, true_label, "branch%u_true", compiler->branch_id); - ++compiler->branch_id; - break; - - case VKD3DSIH_ELSE: - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_IF); - - if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id); - - cf_info->u.if_.else_block_id = vkd3d_spirv_alloc_id(builder); - vkd3d_spirv_as_op_branch_conditional(&builder->function_stream, - cf_info->u.if_.stream_location)->false_label = cf_info->u.if_.else_block_id; - vkd3d_spirv_build_op_name(builder, - cf_info->u.if_.else_block_id, "branch%u_false", cf_info->u.if_.id); - vkd3d_spirv_build_op_label(builder, cf_info->u.if_.else_block_id); - cf_info->inside_block = true; - break; - - case VKD3DSIH_ENDIF: - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_IF); - - if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id); - - vkd3d_spirv_build_op_label(builder, cf_info->u.if_.merge_block_id); - - spirv_compiler_pop_control_flow_level(compiler); - break; - - case VKD3DSIH_LOOP: - if (!(cf_info = spirv_compiler_push_control_flow_level(compiler))) - return VKD3D_ERROR_OUT_OF_MEMORY; - - loop_header_block_id = vkd3d_spirv_alloc_id(builder); - loop_body_block_id = vkd3d_spirv_alloc_id(builder); - continue_block_id = vkd3d_spirv_alloc_id(builder); - merge_block_id = vkd3d_spirv_alloc_id(builder); - - vkd3d_spirv_build_op_branch(builder, loop_header_block_id); - vkd3d_spirv_build_op_label(builder, loop_header_block_id); - vkd3d_spirv_build_op_loop_merge(builder, merge_block_id, continue_block_id, SpvLoopControlMaskNone); - vkd3d_spirv_build_op_branch(builder, loop_body_block_id); - - vkd3d_spirv_build_op_label(builder, loop_body_block_id); - - cf_info->u.loop.header_block_id = loop_header_block_id; - cf_info->u.loop.continue_block_id = continue_block_id; - cf_info->u.loop.merge_block_id = merge_block_id; - cf_info->current_block = VKD3D_BLOCK_LOOP; - cf_info->inside_block = true; - - vkd3d_spirv_build_op_name(builder, loop_header_block_id, "loop%u_header", compiler->loop_id); - vkd3d_spirv_build_op_name(builder, loop_body_block_id, "loop%u_body", compiler->loop_id); - vkd3d_spirv_build_op_name(builder, continue_block_id, "loop%u_continue", compiler->loop_id); - vkd3d_spirv_build_op_name(builder, merge_block_id, "loop%u_merge", compiler->loop_id); - ++compiler->loop_id; - break; - - case VKD3DSIH_ENDLOOP: - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_LOOP); - - /* The loop block may have already been ended by an unconditional - * break instruction right before the end of the loop. */ - if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.loop.continue_block_id); - - vkd3d_spirv_build_op_label(builder, cf_info->u.loop.continue_block_id); - vkd3d_spirv_build_op_branch(builder, cf_info->u.loop.header_block_id); - vkd3d_spirv_build_op_label(builder, cf_info->u.loop.merge_block_id); - - spirv_compiler_pop_control_flow_level(compiler); - break; - - case VKD3DSIH_SWITCH: - if (!(cf_info = spirv_compiler_push_control_flow_level(compiler))) - return VKD3D_ERROR_OUT_OF_MEMORY; - - merge_block_id = vkd3d_spirv_alloc_id(builder); - - assert(src->reg.data_type == VKD3D_DATA_INT); - val_id = spirv_compiler_emit_load_src(compiler, src, VKD3DSP_WRITEMASK_0); - - vkd3d_spirv_build_op_selection_merge(builder, merge_block_id, SpvSelectionControlMaskNone); - - cf_info->u.switch_.id = compiler->switch_id; - cf_info->u.switch_.merge_block_id = merge_block_id; - cf_info->u.switch_.stream_location = vkd3d_spirv_stream_current_location(&builder->function_stream); - cf_info->u.switch_.selector_id = val_id; - cf_info->u.switch_.case_blocks = NULL; - cf_info->u.switch_.case_blocks_size = 0; - cf_info->u.switch_.case_block_count = 0; - cf_info->u.switch_.default_block_id = 0; - cf_info->inside_block = false; - cf_info->current_block = VKD3D_BLOCK_SWITCH; - - vkd3d_spirv_build_op_name(builder, merge_block_id, "switch%u_merge", compiler->switch_id); - - ++compiler->switch_id; - - if (!vkd3d_array_reserve((void **)&cf_info->u.switch_.case_blocks, &cf_info->u.switch_.case_blocks_size, - 10, sizeof(*cf_info->u.switch_.case_blocks))) - return VKD3D_ERROR_OUT_OF_MEMORY; - - break; - - case VKD3DSIH_ENDSWITCH: - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); - assert(!cf_info->inside_block); - - if (!cf_info->u.switch_.default_block_id) - cf_info->u.switch_.default_block_id = cf_info->u.switch_.merge_block_id; - - vkd3d_spirv_build_op_label(builder, cf_info->u.switch_.merge_block_id); - - /* The OpSwitch instruction is inserted when the endswitch - * instruction is processed because we do not know the number - * of case statements in advance.*/ - vkd3d_spirv_begin_function_stream_insertion(builder, cf_info->u.switch_.stream_location); - vkd3d_spirv_build_op_switch(builder, cf_info->u.switch_.selector_id, - cf_info->u.switch_.default_block_id, cf_info->u.switch_.case_blocks, - cf_info->u.switch_.case_block_count); - vkd3d_spirv_end_function_stream_insertion(builder); - - vkd3d_free(cf_info->u.switch_.case_blocks); - spirv_compiler_pop_control_flow_level(compiler); - break; - - case VKD3DSIH_CASE: - { - uint32_t label_id, value; - - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); - - if (src->swizzle != VKD3D_SHADER_SWIZZLE(X, X, X, X)) - { - WARN("Unexpected src swizzle %#x.\n", src->swizzle); - spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE, - "The swizzle for a switch case value is not scalar."); - } - assert(src->reg.type == VKD3DSPR_IMMCONST); - value = *src->reg.u.immconst_uint; - - if (!vkd3d_array_reserve((void **)&cf_info->u.switch_.case_blocks, &cf_info->u.switch_.case_blocks_size, - 2 * (cf_info->u.switch_.case_block_count + 1), sizeof(*cf_info->u.switch_.case_blocks))) - return VKD3D_ERROR_OUT_OF_MEMORY; - - label_id = vkd3d_spirv_alloc_id(builder); - if (cf_info->inside_block) /* fall-through */ - vkd3d_spirv_build_op_branch(builder, label_id); - - cf_info->u.switch_.case_blocks[2 * cf_info->u.switch_.case_block_count + 0] = value; - cf_info->u.switch_.case_blocks[2 * cf_info->u.switch_.case_block_count + 1] = label_id; - ++cf_info->u.switch_.case_block_count; - - vkd3d_spirv_build_op_label(builder, label_id); - cf_info->inside_block = true; - vkd3d_spirv_build_op_name(builder, label_id, "switch%u_case%u", cf_info->u.switch_.id, value); - break; - } - - case VKD3DSIH_DEFAULT: - assert(compiler->control_flow_depth); - assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); - assert(!cf_info->u.switch_.default_block_id); - - cf_info->u.switch_.default_block_id = vkd3d_spirv_alloc_id(builder); - if (cf_info->inside_block) /* fall-through */ - vkd3d_spirv_build_op_branch(builder, cf_info->u.switch_.default_block_id); - - vkd3d_spirv_build_op_label(builder, cf_info->u.switch_.default_block_id); - vkd3d_spirv_build_op_name(builder, cf_info->u.switch_.default_block_id, - "switch%u_default", cf_info->u.switch_.id); - cf_info->inside_block = true; - break; - - case VKD3DSIH_BREAK: - { - struct vkd3d_control_flow_info *breakable_cf_info; - - assert(compiler->control_flow_depth); - - if (!(breakable_cf_info = spirv_compiler_find_innermost_breakable_cf_construct(compiler))) - { - FIXME("Unhandled break instruction.\n"); - return VKD3D_ERROR_INVALID_SHADER; - } - - if (breakable_cf_info->current_block == VKD3D_BLOCK_LOOP) - { - vkd3d_spirv_build_op_branch(builder, breakable_cf_info->u.loop.merge_block_id); - } - else if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) - { - /* The current case block may have already been ended by an - * unconditional continue instruction. */ - if (breakable_cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, breakable_cf_info->u.switch_.merge_block_id); - } - - cf_info->inside_block = false; - break; - } - - case VKD3DSIH_BREAKP: - { - struct vkd3d_control_flow_info *loop_cf_info; - - assert(compiler->control_flow_depth); - - if (!(loop_cf_info = spirv_compiler_find_innermost_loop(compiler))) - { - ERR("Invalid 'breakc' instruction outside loop.\n"); - return VKD3D_ERROR_INVALID_SHADER; - } - - merge_block_id = spirv_compiler_emit_conditional_branch(compiler, - instruction, loop_cf_info->u.loop.merge_block_id); - vkd3d_spirv_build_op_label(builder, merge_block_id); - break; - } - - case VKD3DSIH_CONTINUE: - { - struct vkd3d_control_flow_info *loop_cf_info; - - assert(compiler->control_flow_depth); - - if (!(loop_cf_info = spirv_compiler_find_innermost_loop(compiler))) - { - ERR("Invalid 'continue' instruction outside loop.\n"); - return VKD3D_ERROR_INVALID_SHADER; - } - - vkd3d_spirv_build_op_branch(builder, loop_cf_info->u.loop.continue_block_id); - - cf_info->inside_block = false; - break; - } - - case VKD3DSIH_CONTINUEP: - { - struct vkd3d_control_flow_info *loop_cf_info; - - if (!(loop_cf_info = spirv_compiler_find_innermost_loop(compiler))) - { - ERR("Invalid 'continuec' instruction outside loop.\n"); - return VKD3D_ERROR_INVALID_SHADER; - } - - merge_block_id = spirv_compiler_emit_conditional_branch(compiler, - instruction, loop_cf_info->u.loop.continue_block_id); - vkd3d_spirv_build_op_label(builder, merge_block_id); - break; - } - - case VKD3DSIH_RET: - spirv_compiler_emit_return(compiler, instruction); - - if (cf_info) - cf_info->inside_block = false; - else - compiler->main_block_open = false; - break; - - case VKD3DSIH_RETP: - spirv_compiler_emit_retc(compiler, instruction); - break; - - case VKD3DSIH_DISCARD: - case VKD3DSIH_TEXKILL: - spirv_compiler_emit_kill(compiler, instruction); - break; - - default: - ERR("Unexpected instruction %#x.\n", instruction->handler_idx); - break; + WARN("Unexpected src swizzle %#x.\n", switch_->src.swizzle); + spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE, + "The swizzle for a switch case value is not scalar."); }
- return VKD3D_OK; + val_id = spirv_compiler_emit_load_src(compiler, &switch_->src, VKD3DSP_WRITEMASK_0); + spirv_compiler_emit_merge(compiler, switch_->merge_block_id, 0); + location = vkd3d_spirv_stream_current_location(stream); + vkd3d_spirv_build_op_switch(builder, val_id, + spirv_compiler_get_label_id(compiler, switch_->default_id), NULL, 0); + for (i = 0; i < switch_->case_count; ++i) + { + switch_case = &switch_->cases[i]; + vkd3d_spirv_build_word(stream, switch_case->value); + vkd3d_spirv_build_word(stream, spirv_compiler_get_label_id(compiler, switch_case->block_id)); + } + vkd3d_spirv_stream_add_word_count(stream, location, switch_->case_count * 2u); }
static void spirv_compiler_emit_deriv_instruction(struct spirv_compiler *compiler, @@ -9489,24 +9167,25 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_F32TOF16: spirv_compiler_emit_f32tof16(compiler, instruction); break; - case VKD3DSIH_BREAK: - case VKD3DSIH_BREAKP: - case VKD3DSIH_CASE: - case VKD3DSIH_CONTINUE: - case VKD3DSIH_CONTINUEP: - case VKD3DSIH_DEFAULT: - case VKD3DSIH_DISCARD: - case VKD3DSIH_ELSE: - case VKD3DSIH_ENDIF: - case VKD3DSIH_ENDLOOP: - case VKD3DSIH_ENDSWITCH: - case VKD3DSIH_IF: - case VKD3DSIH_LOOP: case VKD3DSIH_RET: + spirv_compiler_emit_return(compiler, instruction); + compiler->main_block_open = false; + break; case VKD3DSIH_RETP: - case VKD3DSIH_SWITCH: + spirv_compiler_emit_retc(compiler, instruction); + break; + case VKD3DSIH_DISCARD: case VKD3DSIH_TEXKILL: - ret = spirv_compiler_emit_control_flow_instruction(compiler, instruction); + spirv_compiler_emit_kill(compiler, instruction); + break; + case VKD3DSIH_LABEL: + spirv_compiler_emit_label(compiler, instruction); + break; + case VKD3DSIH_BRANCH: + spirv_compiler_emit_branch(compiler, instruction); + break; + case VKD3DSIH_SWITCH: + spirv_compiler_emit_switch(compiler, instruction); break; case VKD3DSIH_DSX: case VKD3DSIH_DSX_COARSE: @@ -9695,6 +9374,9 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, if ((result = vkd3d_shader_normalise(parser, compile_info)) < 0) return result;
+ if (parser->shader_desc.block_count && !spirv_compiler_init_blocks(compiler, parser->shader_desc.block_count)) + return VKD3D_ERROR_OUT_OF_MEMORY; + instructions = parser->instructions; memset(&parser->instructions, 0, sizeof(parser->instructions));
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 92bdd9661..3e2b43c66 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -229,6 +229,7 @@ enum vkd3d_shader_opcode VKD3DSIH_BEM, VKD3DSIH_BFI, VKD3DSIH_BFREV, + VKD3DSIH_BRANCH, VKD3DSIH_BREAK, VKD3DSIH_BREAKC, VKD3DSIH_BREAKP, @@ -956,6 +957,7 @@ struct vkd3d_shader_desc
uint32_t temp_count; unsigned int ssa_count; + unsigned int block_count;
struct { @@ -1060,6 +1062,30 @@ struct vkd3d_shader_location unsigned int line, column; };
+struct vkd3d_shader_branch +{ + unsigned int merge_block_id; + unsigned int continue_block_id; + struct vkd3d_shader_src_param condition; + unsigned int true_id; + unsigned int false_id; +}; + +struct vkd3d_shader_switch_case +{ + unsigned int value; + unsigned int block_id; +}; + +struct vkd3d_shader_switch +{ + unsigned int merge_block_id; + struct vkd3d_shader_src_param src; + const struct vkd3d_shader_switch_case *cases; + unsigned int case_count; + unsigned int default_id; +}; + struct vkd3d_shader_instruction { struct vkd3d_shader_location location; @@ -1099,6 +1125,8 @@ struct vkd3d_shader_instruction struct vkd3d_shader_index_range index_range; struct vkd3d_shader_indexable_temp indexable_temp; struct vkd3d_shader_function_table_pointer fp; + struct vkd3d_shader_branch branch; + struct vkd3d_shader_switch switch_; } declaration; };
I need to recheck this, add more error handling, and set `compiler->main_block_open` correctly, but it passes all the tests.
Questions:
In d3dbc what does the `LABEL` instruction src parameter contain? I assume this instruction adds a block label so I'm using it for that, and currently emit it with an immediate constant.
We lose the naming of block label ids. One way to handle this is include the necessary info in the IR to recreate the deleted namings in the backend. Another is add support for names to VSIR. Thoughts?
The new instruction count sometimes gets ahead of the source count. This is handled by writing everything to a new instruction array, and swapping out the old one at the end. Simple and effective for the moment?
Questions:
In d3dbc what does the `LABEL` instruction src parameter contain? I assume this instruction adds a block label so I'm using it for that, and currently emit it with an immediate constant.
These look like "label l0", "label l1", etc. in the disassembler. I.e., using VKD3DSPR_LABEL. They'd get called like "call l0", "call l1", etc. LABEL/CALL isn't terribly common though, to the point that I don't think Wine even has tests for them; the vsa/psa assemblers may be the only way to get them.
We lose the naming of block label ids. One way to handle this is include the necessary info in the IR to recreate the deleted namings in the backend. Another is add support for names to VSIR. Thoughts?
I imagine we could do something similar to SPIR-V's OpName if needed, but I don't think we have a strong reason to choose that option over just including e.g. an array with debug names in struct vkd3d_shader_desc.
The new instruction count sometimes gets ahead of the source count. This is handled by writing everything to a new instruction array, and swapping out the old one at the end. Simple and effective for the moment?
Sure.