-- v8: vkd3d-shader/dxil: Handle the DXIL SWITCH instruction. vkd3d-shader/dxil: Handle the DXIL PHI instruction. vkd3d-shader/dxil: Handle the DXIL BR instruction conditional variant. vkd3d-shader/dxil: Handle the DXIL BR instruction unconditional variant. vkd3d-shader/dxil: Introduce a code block terminator struct. vkd3d-shader/ir: Include an initial label instruction in the first control flow block. vkd3d-shader/ir: Store code block names in struct vkd3d_shader_desc. vkd3d-shader/ir: Flatten SWITCH/CASE/DEFAULT/ENDSWITCH control flow instructions. vkd3d-shader/ir: Flatten LOOP/BREAK/CONTINUE/ENDLOOP control flow instructions. vkd3d-shader/ir: Flatten IF/ELSE/ENDIF control flow instructions. vkd3d-shader/spirv: Handle RETP in spirv_compiler_handle_instruction(). vkd3d-shader/spirv: Handle DISCARD and TEXKILL in spirv_compiler_handle_instruction(). vkd3d-shader/spirv: Emit descriptor offset loads in the function entry block.
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 aa8ceaa67..0b0d34a7c 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2431,6 +2431,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) { @@ -3511,11 +3512,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); } @@ -6655,6 +6658,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/spirv.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 0b0d34a7c..005746ea5 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -7962,11 +7962,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c 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; @@ -9725,7 +9720,6 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_CONTINUE: case VKD3DSIH_CONTINUEP: case VKD3DSIH_DEFAULT: - case VKD3DSIH_DISCARD: case VKD3DSIH_ELSE: case VKD3DSIH_ENDIF: case VKD3DSIH_ENDLOOP: @@ -9735,9 +9729,12 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_RET: case VKD3DSIH_RETP: case VKD3DSIH_SWITCH: - case VKD3DSIH_TEXKILL: ret = spirv_compiler_emit_control_flow_instruction(compiler, instruction); break; + case VKD3DSIH_DISCARD: + case VKD3DSIH_TEXKILL: + spirv_compiler_emit_kill(compiler, instruction); + break; case VKD3DSIH_DSX: case VKD3DSIH_DSX_COARSE: case VKD3DSIH_DSX_FINE:
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/spirv.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 005746ea5..12f10a881 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -7958,10 +7958,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c compiler->main_block_open = false; break;
- case VKD3DSIH_RETP: - spirv_compiler_emit_retc(compiler, instruction); - break; - default: ERR("Unexpected instruction %#x.\n", instruction->handler_idx); break; @@ -9727,10 +9723,12 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_IF: case VKD3DSIH_LOOP: case VKD3DSIH_RET: - case VKD3DSIH_RETP: case VKD3DSIH_SWITCH: ret = spirv_compiler_emit_control_flow_instruction(compiler, instruction); break; + case VKD3DSIH_RETP: + spirv_compiler_emit_retc(compiler, instruction); + break; case VKD3DSIH_DISCARD: case VKD3DSIH_TEXKILL: spirv_compiler_emit_kill(compiler, instruction);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 1 + libs/vkd3d-shader/ir.c | 353 +++++++++++++++++++++++ libs/vkd3d-shader/spirv.c | 179 ++++++------ libs/vkd3d-shader/vkd3d_shader_private.h | 7 + 4 files changed, 452 insertions(+), 88 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 0589bc421..2fbd98936 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", diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index bac426919..33a6aec8d 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -312,6 +312,21 @@ void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_reg reg->alignment = 0; }
+static void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, + enum vkd3d_data_type data_type, unsigned int idx_count) +{ + vsir_register_init(¶m->reg, reg_type, data_type, idx_count); + param->swizzle = 0; + param->modifiers = VKD3DSPSM_NONE; +} + +static void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id) +{ + vsir_src_param_init(param, VKD3DSPR_LABEL, VKD3D_DATA_UINT, 1); + param->reg.dimension = VSIR_DIMENSION_NONE; + param->reg.idx[0].offset = label_id; +} + void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, enum vkd3d_shader_opcode handler_idx) { @@ -320,6 +335,23 @@ void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vk ins->handler_idx = handler_idx; }
+static bool vsir_instruction_init_label(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, + unsigned int label_id, void *parser) +{ + struct vkd3d_shader_src_param *src_param; + + if (!(src_param = shader_parser_get_src_params(parser, 1))) + return false; + + vsir_src_param_init_label(src_param, label_id); + + vsir_instruction_init(ins, location, VKD3DSIH_LABEL); + ins->src = src_param; + ins->src_count = 1; + + return true; +} + static enum vkd3d_result instruction_array_flatten_hull_shader_phases(struct vkd3d_shader_instruction_array *src_instructions) { struct hull_flattener flattener = {*src_instructions}; @@ -1476,6 +1508,324 @@ static enum vkd3d_result normalise_combined_samplers(struct vkd3d_shader_parser return VKD3D_OK; }
+struct cf_flattener_if_info +{ + struct vkd3d_shader_src_param *false_param; + uint32_t merge_block_id; + unsigned int else_block_id; +}; + +struct cf_flattener_info +{ + union + { + struct cf_flattener_if_info if_; + } u; + + enum + { + VKD3D_BLOCK_IF, + } current_block; + bool inside_block; +}; + +struct cf_flattener +{ + struct vkd3d_shader_parser *parser; + + struct vkd3d_shader_location location; + bool allocation_failed; + + struct vkd3d_shader_instruction *instructions; + size_t instruction_capacity; + size_t instruction_count; + + unsigned int block_id; + + unsigned int control_flow_depth; + struct cf_flattener_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"); + flattener->allocation_failed = true; + return NULL; + } + return &flattener->instructions[flattener->instruction_count]; +} + +static bool cf_flattener_copy_instruction(struct cf_flattener *flattener, + const struct vkd3d_shader_instruction *instruction) +{ + struct vkd3d_shader_instruction *dst_ins; + + if (instruction->handler_idx == VKD3DSIH_NOP) + return true; + + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return false; + + *dst_ins = *instruction; + ++flattener->instruction_count; + return true; +} + +static unsigned int cf_flattener_alloc_block_id(struct cf_flattener *flattener) +{ + return ++flattener->block_id; +} + +static struct vkd3d_shader_src_param *instruction_src_params_alloc(struct vkd3d_shader_instruction *ins, + unsigned int count, struct cf_flattener *flattener) +{ + struct vkd3d_shader_src_param *params = shader_parser_get_src_params(flattener->parser, count); + if (!params) + { + flattener->allocation_failed = true; + return NULL; + } + ins->src = params; + ins->src_count = count; + return params; +} + +static void cf_flattener_emit_label(struct cf_flattener *flattener, unsigned int label_id) +{ + struct vkd3d_shader_instruction *ins; + + if (!(ins = cf_flattener_require_space(flattener, 1))) + return; + if (vsir_instruction_init_label(ins, &flattener->location, label_id, flattener->parser)) + ++flattener->instruction_count; + else + flattener->allocation_failed = true; +} + +/* For conditional branches, this returns the false target branch parameter. */ +static struct vkd3d_shader_src_param *cf_flattener_emit_branch(struct cf_flattener *flattener, + unsigned int merge_block_id, const struct vkd3d_shader_src_param *condition, + unsigned int true_id, unsigned int false_id, + unsigned int flags) +{ + struct vkd3d_shader_src_param *src_params, *false_branch_param; + struct vkd3d_shader_instruction *ins; + + if (!(ins = cf_flattener_require_space(flattener, 1))) + return NULL; + vsir_instruction_init(ins, &flattener->location, VKD3DSIH_BRANCH); + + if (condition) + { + if (!(src_params = instruction_src_params_alloc(ins, 4, flattener))) + return NULL; + src_params[0] = *condition; + if (flags == VKD3D_SHADER_CONDITIONAL_OP_Z) + { + vsir_src_param_init_label(&src_params[1], false_id); + vsir_src_param_init_label(&src_params[2], true_id); + false_branch_param = &src_params[1]; + } + else + { + vsir_src_param_init_label(&src_params[1], true_id); + vsir_src_param_init_label(&src_params[2], false_id); + false_branch_param = &src_params[2]; + } + vsir_src_param_init_label(&src_params[3], merge_block_id); + } + else + { + if (!(src_params = instruction_src_params_alloc(ins, 1, flattener))) + return NULL; + vsir_src_param_init_label(&src_params[0], true_id); + false_branch_param = NULL; + } + + ++flattener->instruction_count; + + return false_branch_param; +} + +static void cf_flattener_emit_unconditional_branch(struct cf_flattener *flattener, unsigned int target_block_id) +{ + cf_flattener_emit_branch(flattener, 0, NULL, target_block_id, 0, 0); +} + +static struct cf_flattener_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"); + flattener->allocation_failed = true; + return NULL; + } + + return &flattener->control_flow_info[flattener->control_flow_depth++]; +} + +static void cf_flattener_pop_control_flow_level(struct cf_flattener *flattener) +{ + struct cf_flattener_info *cf_info; + + cf_info = &flattener->control_flow_info[--flattener->control_flow_depth]; + memset(cf_info, 0, sizeof(*cf_info)); +} + +static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flattener *flattener) +{ + struct vkd3d_shader_parser *parser = flattener->parser; + struct vkd3d_shader_instruction_array *instructions; + struct vkd3d_shader_instruction *dst_ins; + unsigned int depth = 0; + bool main_block_open; + size_t i; + + instructions = &parser->instructions; + main_block_open = parser->shader_version.type != VKD3D_SHADER_TYPE_HULL; + + if (!cf_flattener_require_space(flattener, instructions->count)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < instructions->count; ++i) + { + const struct vkd3d_shader_instruction *instruction = &instructions->elements[i]; + const struct vkd3d_shader_src_param *src = instruction->src; + unsigned int merge_block_id, true_block_id; + struct cf_flattener_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_LABEL: + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, + "Aborting due to not yet implemented feature: Label instruction."); + return VKD3D_ERROR_NOT_IMPLEMENTED; + + 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_.false_param = cf_flattener_emit_branch(flattener, merge_block_id, + src, true_block_id, merge_block_id, instruction->flags); + if (!cf_info->u.if_.false_param) + return VKD3D_ERROR_OUT_OF_MEMORY; + + cf_flattener_emit_label(flattener, true_block_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; + break; + + case VKD3DSIH_ELSE: + if (cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, cf_info->u.if_.merge_block_id); + + cf_info->u.if_.else_block_id = cf_flattener_alloc_block_id(flattener); + cf_info->u.if_.false_param->reg.idx[0].offset = 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: + if (cf_info->inside_block) + cf_flattener_emit_unconditional_branch(flattener, cf_info->u.if_.merge_block_id); + + cf_flattener_emit_label(flattener, cf_info->u.if_.merge_block_id); + + cf_flattener_pop_control_flow_level(flattener); + break; + + case VKD3DSIH_LOOP: + case VKD3DSIH_SWITCH: + ++depth; + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + break; + + case VKD3DSIH_ENDLOOP: + case VKD3DSIH_ENDSWITCH: + --depth; + if (cf_info) + cf_info->inside_block = true; /* Dead code removal ensures this is correct. */ + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + break; + + case VKD3DSIH_RET: + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + if (cf_info) + cf_info->inside_block = false; + else if (!depth) + main_block_open = false; + break; + + case VKD3DSIH_BREAK: + case VKD3DSIH_CONTINUE: + if (cf_info) + cf_info->inside_block = false; + /* fall through */ + default: + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + break; + } + } + + if (main_block_open) + { + if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + vsir_instruction_init(dst_ins, &flattener->location, VKD3DSIH_RET); + ++flattener->instruction_count; + } + + return flattener->allocation_failed ? VKD3D_ERROR_OUT_OF_MEMORY : VKD3D_OK; +} + +static enum vkd3d_result flatten_control_flow_constructs(struct vkd3d_shader_parser *parser) +{ + struct cf_flattener flattener = {0}; + enum vkd3d_result result; + + flattener.parser = parser; + result = cf_flattener_iterate_instruction_array(&flattener); + + 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); + } + + vkd3d_free(flattener.control_flow_info); + + return result; +} + enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, const struct vkd3d_shader_compile_info *compile_info) { @@ -1504,6 +1854,9 @@ enum vkd3d_result vkd3d_shader_normalise(struct vkd3d_shader_parser *parser, if (result >= 0) remove_dead_code(parser);
+ if (result >= 0) + result = flatten_control_flow_constructs(parser); + if (result >= 0) result = normalise_combined_samplers(parser);
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 12f10a881..3dc60c681 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -855,20 +855,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) { @@ -2258,14 +2244,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; @@ -2289,14 +2267,12 @@ 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; @@ -2356,7 +2332,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; @@ -2371,7 +2346,6 @@ struct spirv_compiler struct vkd3d_push_constant_buffer_binding *push_constants; const struct vkd3d_shader_spirv_target_info *spirv_target_info;
- bool main_block_open; bool after_declarations_section; struct shader_signature input_signature; struct shader_signature output_signature; @@ -2413,6 +2387,9 @@ struct spirv_compiler unsigned int ssa_register_count;
uint64_t config_flags; + + uint32_t *block_label_ids; + unsigned int block_count; };
static bool is_in_default_phase(const struct spirv_compiler *compiler) @@ -2462,6 +2439,7 @@ static void spirv_compiler_destroy(struct spirv_compiler *compiler) shader_signature_cleanup(&compiler->patch_constant_signature);
vkd3d_free(compiler->ssa_register_info); + vkd3d_free(compiler->block_label_ids);
vkd3d_free(compiler); } @@ -2797,6 +2775,14 @@ 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) +{ + --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, @@ -5541,7 +5527,6 @@ static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *comp if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) { vkd3d_spirv_builder_begin_main_function(builder); - compiler->main_block_open = true; } }
@@ -7652,72 +7637,27 @@ static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_c return NULL; }
+static void spirv_compiler_emit_label(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction); + static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { uint32_t loop_header_block_id, loop_body_block_id, continue_block_id; 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; + uint32_t merge_block_id, val_id;
cf_info = compiler->control_flow_depth ? &compiler->control_flow_info[compiler->control_flow_depth - 1] : NULL;
switch (instruction->handler_idx) { - 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, src->reg.data_type, 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); + case VKD3DSIH_LABEL: + if (cf_info) + cf_info->inside_block = true; + spirv_compiler_emit_label(compiler, instruction); break;
case VKD3DSIH_LOOP: @@ -7954,8 +7894,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c
if (cf_info) cf_info->inside_block = false; - else - compiler->main_block_open = false; break;
default: @@ -7966,6 +7904,70 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c return VKD3D_OK; }
+static bool spirv_compiler_init_blocks(struct spirv_compiler *compiler, unsigned int block_count) +{ + compiler->block_count = block_count; + + if (!(compiler->block_label_ids = vkd3d_calloc(block_count, sizeof(*compiler->block_label_ids)))) + return false; + + return true; +} + +static void spirv_compiler_emit_label(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) +{ + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + const struct vkd3d_shader_src_param *src = instruction->src; + unsigned int block_id = src->reg.idx[0].offset; + uint32_t label_id; + + label_id = spirv_compiler_get_label_id(compiler, block_id); + vkd3d_spirv_build_op_label(builder, label_id); +} + +static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, + uint32_t merge_block_id) +{ + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + + if (!merge_block_id) + return; + + merge_block_id = spirv_compiler_get_label_id(compiler, merge_block_id); + vkd3d_spirv_build_op_selection_merge(builder, merge_block_id, SpvSelectionControlMaskNone); +} + +static void spirv_compiler_emit_branch(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) +{ + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + const struct vkd3d_shader_src_param *src = instruction->src; + uint32_t condition_id; + + if (vsir_register_is_label(&src[0].reg)) + { + vkd3d_spirv_build_op_branch(builder, spirv_compiler_get_label_id(compiler, src[0].reg.idx[0].offset)); + return; + } + + if (!vkd3d_swizzle_is_scalar(src->swizzle)) + { + WARN("Unexpected src swizzle %#x.\n", src->swizzle); + spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE, + "The swizzle for a branch condition value is not scalar."); + } + + condition_id = spirv_compiler_emit_load_src(compiler, &src[0], VKD3DSP_WRITEMASK_0); + condition_id = spirv_compiler_emit_int_to_bool(compiler, + VKD3D_SHADER_CONDITIONAL_OP_NZ, src[0].reg.data_type, 1, condition_id); + /* Emit the merge immediately before the branch instruction. */ + spirv_compiler_emit_merge(compiler, src[3].reg.idx[0].offset); + vkd3d_spirv_build_op_branch_conditional(builder, condition_id, + spirv_compiler_get_label_id(compiler, src[1].reg.idx[0].offset), + spirv_compiler_get_label_id(compiler, src[2].reg.idx[0].offset)); +} + static void spirv_compiler_emit_deriv_instruction(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { @@ -9716,11 +9718,9 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_CONTINUE: case VKD3DSIH_CONTINUEP: case VKD3DSIH_DEFAULT: - case VKD3DSIH_ELSE: - case VKD3DSIH_ENDIF: case VKD3DSIH_ENDLOOP: case VKD3DSIH_ENDSWITCH: - case VKD3DSIH_IF: + case VKD3DSIH_LABEL: case VKD3DSIH_LOOP: case VKD3DSIH_RET: case VKD3DSIH_SWITCH: @@ -9733,6 +9733,9 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_TEXKILL: spirv_compiler_emit_kill(compiler, instruction); break; + case VKD3DSIH_BRANCH: + spirv_compiler_emit_branch(compiler, instruction); + break; case VKD3DSIH_DSX: case VKD3DSIH_DSX_COARSE: case VKD3DSIH_DSX_FINE: @@ -9953,6 +9956,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));
@@ -9983,9 +9989,6 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, if (result < 0) return result;
- if (compiler->main_block_open) - vkd3d_spirv_build_op_return(builder); - if (!is_in_default_phase(compiler)) spirv_compiler_leave_shader_phase(compiler); else diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 199a47a76..30672950b 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -237,6 +237,7 @@ enum vkd3d_shader_opcode VKD3DSIH_BEM, VKD3DSIH_BFI, VKD3DSIH_BFREV, + VKD3DSIH_BRANCH, VKD3DSIH_BREAK, VKD3DSIH_BREAKC, VKD3DSIH_BREAKP, @@ -1015,6 +1016,7 @@ struct vkd3d_shader_desc
uint32_t temp_count; unsigned int ssa_count; + unsigned int block_count;
struct { @@ -1184,6 +1186,11 @@ static inline bool register_is_constant(const struct vkd3d_shader_register *reg) return (reg->type == VKD3DSPR_IMMCONST || reg->type == VKD3DSPR_IMMCONST64); }
+static inline bool vsir_register_is_label(const struct vkd3d_shader_register *reg) +{ + return reg->type == VKD3DSPR_LABEL; +} + struct vkd3d_shader_param_node { struct vkd3d_shader_param_node *next;
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/ir.c | 194 ++++++++++++++++++++++++++++++++++---- libs/vkd3d-shader/spirv.c | 151 ++++------------------------- 2 files changed, 194 insertions(+), 151 deletions(-)
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 33a6aec8d..39c02a14f 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1515,16 +1515,26 @@ struct cf_flattener_if_info unsigned int else_block_id; };
+struct cf_flattener_loop_info +{ + unsigned int header_block_id; + unsigned int continue_block_id; + uint32_t merge_block_id; +}; + struct cf_flattener_info { union { struct cf_flattener_if_info if_; + struct cf_flattener_loop_info loop; } u;
enum { VKD3D_BLOCK_IF, + VKD3D_BLOCK_LOOP, + VKD3D_BLOCK_SWITCH, } current_block; bool inside_block; }; @@ -1608,8 +1618,8 @@ static void cf_flattener_emit_label(struct cf_flattener *flattener, unsigned int
/* For conditional branches, this returns the false target branch parameter. */ static struct vkd3d_shader_src_param *cf_flattener_emit_branch(struct cf_flattener *flattener, - unsigned int merge_block_id, const struct vkd3d_shader_src_param *condition, - unsigned int true_id, unsigned int false_id, + unsigned int merge_block_id, unsigned int continue_block_id, + const struct vkd3d_shader_src_param *condition, unsigned int true_id, unsigned int false_id, unsigned int flags) { struct vkd3d_shader_src_param *src_params, *false_branch_param; @@ -1621,7 +1631,7 @@ static struct vkd3d_shader_src_param *cf_flattener_emit_branch(struct cf_flatten
if (condition) { - if (!(src_params = instruction_src_params_alloc(ins, 4, flattener))) + if (!(src_params = instruction_src_params_alloc(ins, 4 + !!continue_block_id, flattener))) return NULL; src_params[0] = *condition; if (flags == VKD3D_SHADER_CONDITIONAL_OP_Z) @@ -1637,12 +1647,21 @@ static struct vkd3d_shader_src_param *cf_flattener_emit_branch(struct cf_flatten false_branch_param = &src_params[2]; } vsir_src_param_init_label(&src_params[3], merge_block_id); + if (continue_block_id) + vsir_src_param_init_label(&src_params[4], continue_block_id); } else { - if (!(src_params = instruction_src_params_alloc(ins, 1, flattener))) + if (!(src_params = instruction_src_params_alloc(ins, merge_block_id ? 3 : 1, flattener))) return NULL; vsir_src_param_init_label(&src_params[0], true_id); + if (merge_block_id) + { + /* An unconditional branch may only have merge information for a loop, which + * must have both a merge block and continue block. */ + vsir_src_param_init_label(&src_params[1], merge_block_id); + vsir_src_param_init_label(&src_params[2], continue_block_id); + } false_branch_param = NULL; }
@@ -1651,9 +1670,19 @@ static struct vkd3d_shader_src_param *cf_flattener_emit_branch(struct cf_flatten return false_branch_param; }
+static void cf_flattener_emit_conditional_branch_and_merge(struct cf_flattener *flattener, + const struct vkd3d_shader_src_param *condition, unsigned int true_id, unsigned int flags) +{ + unsigned int merge_block_id; + + merge_block_id = cf_flattener_alloc_block_id(flattener); + cf_flattener_emit_branch(flattener, merge_block_id, 0, condition, true_id, merge_block_id, flags); + cf_flattener_emit_label(flattener, merge_block_id); +} + static void cf_flattener_emit_unconditional_branch(struct cf_flattener *flattener, unsigned int target_block_id) { - cf_flattener_emit_branch(flattener, 0, NULL, target_block_id, 0, 0); + cf_flattener_emit_branch(flattener, 0, 0, NULL, target_block_id, 0, 0); }
static struct cf_flattener_info *cf_flattener_push_control_flow_level(struct cf_flattener *flattener) @@ -1677,12 +1706,38 @@ static void cf_flattener_pop_control_flow_level(struct cf_flattener *flattener) memset(cf_info, 0, sizeof(*cf_info)); }
+static struct cf_flattener_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 cf_flattener_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 cf_flattener_iterate_instruction_array(struct cf_flattener *flattener) { struct vkd3d_shader_parser *parser = flattener->parser; struct vkd3d_shader_instruction_array *instructions; struct vkd3d_shader_instruction *dst_ins; - unsigned int depth = 0; bool main_block_open; size_t i;
@@ -1694,9 +1749,9 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte
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; - unsigned int merge_block_id, true_block_id; struct cf_flattener_info *cf_info;
flattener->location = instruction->location; @@ -1717,7 +1772,7 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte
true_block_id = cf_flattener_alloc_block_id(flattener); merge_block_id = cf_flattener_alloc_block_id(flattener); - cf_info->u.if_.false_param = cf_flattener_emit_branch(flattener, merge_block_id, + cf_info->u.if_.false_param = cf_flattener_emit_branch(flattener, merge_block_id, 0, src, true_block_id, merge_block_id, instruction->flags); if (!cf_info->u.if_.false_param) return VKD3D_ERROR_OUT_OF_MEMORY; @@ -1752,36 +1807,137 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte 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); + cf_flattener_emit_branch(flattener, merge_block_id, continue_block_id, + NULL, loop_body_block_id, 0, 0); + + 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->u.loop.merge_block_id = merge_block_id; + cf_info->current_block = VKD3D_BLOCK_LOOP; + cf_info->inside_block = true; + break; + + case VKD3DSIH_ENDLOOP: + 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->u.loop.merge_block_id); + + cf_flattener_pop_control_flow_level(flattener); + break; + case VKD3DSIH_SWITCH: - ++depth; + if (!(cf_info = cf_flattener_push_control_flow_level(flattener))) + return VKD3D_ERROR_OUT_OF_MEMORY; + if (!cf_flattener_copy_instruction(flattener, instruction)) return VKD3D_ERROR_OUT_OF_MEMORY; + + cf_info->current_block = VKD3D_BLOCK_SWITCH; + break;
- case VKD3DSIH_ENDLOOP: case VKD3DSIH_ENDSWITCH: - --depth; - if (cf_info) - cf_info->inside_block = true; /* Dead code removal ensures this is correct. */ + cf_flattener_pop_control_flow_level(flattener); + if (!cf_flattener_copy_instruction(flattener, instruction)) return VKD3D_ERROR_OUT_OF_MEMORY; break;
+ case VKD3DSIH_BREAK: + { + struct cf_flattener_info *breakable_cf_info; + + 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->u.loop.merge_block_id); + } + else if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) + { + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + cf_info->inside_block = false; + break; + } + + case VKD3DSIH_BREAKP: + { + struct cf_flattener_info *loop_cf_info; + + 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_merge(flattener, + src, loop_cf_info->u.loop.merge_block_id, instruction->flags); + break; + } + + case VKD3DSIH_CONTINUE: + { + struct cf_flattener_info *loop_cf_info; + + 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 cf_flattener_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_merge(flattener, + src, loop_cf_info->u.loop.continue_block_id, instruction->flags); + break; + } + case VKD3DSIH_RET: if (!cf_flattener_copy_instruction(flattener, instruction)) return VKD3D_ERROR_OUT_OF_MEMORY;
if (cf_info) cf_info->inside_block = false; - else if (!depth) + else main_block_open = false; break;
- case VKD3DSIH_BREAK: - case VKD3DSIH_CONTINUE: - if (cf_info) - cf_info->inside_block = false; - /* fall through */ default: if (!cf_flattener_copy_instruction(flattener, instruction)) return VKD3D_ERROR_OUT_OF_MEMORY; diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 3dc60c681..b1a915c6f 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2244,13 +2244,6 @@ static const char *debug_vkd3d_symbol(const struct vkd3d_symbol *symbol) } }
-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; @@ -2267,13 +2260,11 @@ struct vkd3d_control_flow_info { union { - struct vkd3d_loop_cf_info loop; struct vkd3d_switch_cf_info switch_; } u;
enum { - VKD3D_BLOCK_LOOP, VKD3D_BLOCK_SWITCH, } current_block; bool inside_block; @@ -2332,7 +2323,6 @@ struct spirv_compiler
enum vkd3d_shader_type shader_type;
- unsigned int loop_id; unsigned int switch_id; unsigned int control_flow_depth; struct vkd3d_control_flow_info *control_flow_info; @@ -7608,20 +7598,6 @@ static void spirv_compiler_pop_control_flow_level(struct spirv_compiler *compile memset(cf_info, 0, sizeof(*cf_info)); }
-static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_loop( - struct spirv_compiler *compiler) -{ - int depth; - - for (depth = compiler->control_flow_depth - 1; depth >= 0; --depth) - { - if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP) - return &compiler->control_flow_info[depth]; - } - - return NULL; -} - static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_cf_construct( struct spirv_compiler *compiler) { @@ -7629,8 +7605,7 @@ static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_c
for (depth = compiler->control_flow_depth - 1; depth >= 0; --depth) { - if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_LOOP - || compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_SWITCH) + if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_SWITCH) return &compiler->control_flow_info[depth]; }
@@ -7643,7 +7618,6 @@ static void spirv_compiler_emit_label(struct spirv_compiler *compiler, static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - uint32_t loop_header_block_id, loop_body_block_id, continue_block_id; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_shader_src_param *src = instruction->src; struct vkd3d_control_flow_info *cf_info; @@ -7660,51 +7634,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c spirv_compiler_emit_label(compiler, instruction); 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; @@ -7821,11 +7750,7 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c 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) + if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) { /* The current case block may have already been ended by an * unconditional continue instruction. */ @@ -7837,58 +7762,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c 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);
@@ -7927,7 +7800,7 @@ static void spirv_compiler_emit_label(struct spirv_compiler *compiler, }
static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, - uint32_t merge_block_id) + uint32_t merge_block_id, uint32_t continue_block_id) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
@@ -7935,7 +7808,15 @@ static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, return;
merge_block_id = spirv_compiler_get_label_id(compiler, merge_block_id); - vkd3d_spirv_build_op_selection_merge(builder, merge_block_id, SpvSelectionControlMaskNone); + if (!continue_block_id) + { + 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); + } }
static void spirv_compiler_emit_branch(struct spirv_compiler *compiler, @@ -7947,6 +7828,11 @@ static void spirv_compiler_emit_branch(struct spirv_compiler *compiler,
if (vsir_register_is_label(&src[0].reg)) { + if (instruction->src_count > 1) + { + /* Loop merge only. Must have a merge block and a continue block. */ + spirv_compiler_emit_merge(compiler, src[1].reg.idx[0].offset, src[2].reg.idx[0].offset); + } vkd3d_spirv_build_op_branch(builder, spirv_compiler_get_label_id(compiler, src[0].reg.idx[0].offset)); return; } @@ -7962,7 +7848,8 @@ static void spirv_compiler_emit_branch(struct spirv_compiler *compiler, condition_id = spirv_compiler_emit_int_to_bool(compiler, VKD3D_SHADER_CONDITIONAL_OP_NZ, src[0].reg.data_type, 1, condition_id); /* Emit the merge immediately before the branch instruction. */ - spirv_compiler_emit_merge(compiler, src[3].reg.idx[0].offset); + spirv_compiler_emit_merge(compiler, src[3].reg.idx[0].offset, + (instruction->src_count > 4) ? src[4].reg.idx[0].offset : 0); vkd3d_spirv_build_op_branch_conditional(builder, condition_id, spirv_compiler_get_label_id(compiler, src[1].reg.idx[0].offset), spirv_compiler_get_label_id(compiler, src[2].reg.idx[0].offset));
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 1 + libs/vkd3d-shader/ir.c | 110 ++++++++- libs/vkd3d-shader/spirv.c | 295 ++++------------------- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 4 files changed, 155 insertions(+), 252 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 2fbd98936..120eca06b 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -279,6 +279,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_SUB ] = "sub", [VKD3DSIH_SWAPC ] = "swapc", [VKD3DSIH_SWITCH ] = "switch", + [VKD3DSIH_SWITCH_MONOLITHIC ] = "switch", [VKD3DSIH_SYNC ] = "sync", [VKD3DSIH_TEX ] = "texld", [VKD3DSIH_TEXBEM ] = "texbem", diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 39c02a14f..e6758a638 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1522,12 +1522,30 @@ struct cf_flattener_loop_info uint32_t merge_block_id; };
+struct cf_flattener_switch_case +{ + unsigned int value; + unsigned int block_id; +}; + +struct cf_flattener_switch_info +{ + size_t ins_location; + const struct vkd3d_shader_src_param *condition; + unsigned int merge_block_id; + unsigned int default_block_id; + struct cf_flattener_switch_case *cases; + size_t cases_size; + unsigned int cases_count; +}; + struct cf_flattener_info { union { struct cf_flattener_if_info if_; struct cf_flattener_loop_info loop; + struct cf_flattener_switch_info switch_; } u;
enum @@ -1844,18 +1862,101 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte if (!(cf_info = cf_flattener_push_control_flow_level(flattener))) return VKD3D_ERROR_OUT_OF_MEMORY;
- if (!cf_flattener_copy_instruction(flattener, instruction)) - return VKD3D_ERROR_OUT_OF_MEMORY; + merge_block_id = cf_flattener_alloc_block_id(flattener); + + cf_info->u.switch_.ins_location = flattener->instruction_count; + cf_info->u.switch_.condition = src;
+ if (!(dst_ins = cf_flattener_require_space(flattener, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + vsir_instruction_init(dst_ins, &instruction->location, VKD3DSIH_SWITCH_MONOLITHIC); + ++flattener->instruction_count; + + cf_info->u.switch_.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;
+ 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: + { + struct vkd3d_shader_src_param *src_params; + unsigned int j; + + if (!cf_info->u.switch_.default_block_id) + cf_info->u.switch_.default_block_id = cf_info->u.switch_.merge_block_id; + + cf_flattener_emit_label(flattener, cf_info->u.switch_.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]; + if (!(src_params = instruction_src_params_alloc(dst_ins, cf_info->u.switch_.cases_count * 2 + 3, flattener))) + { + vkd3d_free(cf_info->u.switch_.cases); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + src_params[0] = *cf_info->u.switch_.condition; + vsir_src_param_init_label(&src_params[1], cf_info->u.switch_.default_block_id); + vsir_src_param_init_label(&src_params[2], cf_info->u.switch_.merge_block_id); + for (j = 0; j < cf_info->u.switch_.cases_count; ++j) + { + unsigned int index = j * 2 + 3; + vsir_src_param_init(&src_params[index], VKD3DSPR_IMMCONST, VKD3D_DATA_UINT, 0); + src_params[index].reg.u.immconst_u32[0] = cf_info->u.switch_.cases[j].value; + vsir_src_param_init_label(&src_params[index + 1], cf_info->u.switch_.cases[j].block_id); + } + vkd3d_free(cf_info->u.switch_.cases); + cf_flattener_pop_control_flow_level(flattener); + break; + }
- if (!cf_flattener_copy_instruction(flattener, instruction)) + case VKD3DSIH_CASE: + { + unsigned int label_id, value; + + if (src->swizzle != VKD3D_SHADER_SWIZZLE(X, X, X, X)) + { + WARN("Unexpected src swizzle %#x.\n", src->swizzle); + vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_INVALID_SWIZZLE, + "The swizzle for a switch case value is not scalar X."); + } + value = *src->reg.u.immconst_u32; + + 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: + 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: @@ -1874,8 +1975,7 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte } else if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) { - if (!cf_flattener_copy_instruction(flattener, instruction)) - return VKD3D_ERROR_OUT_OF_MEMORY; + cf_flattener_emit_unconditional_branch(flattener, breakable_cf_info->u.switch_.merge_block_id); }
cf_info->inside_block = false; diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index b1a915c6f..fb2028999 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2244,32 +2244,6 @@ static const char *debug_vkd3d_symbol(const struct vkd3d_symbol *symbol) } }
-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_switch_cf_info switch_; - } u; - - enum - { - VKD3D_BLOCK_SWITCH, - } current_block; - bool inside_block; -}; - struct vkd3d_push_constant_buffer_binding { struct vkd3d_shader_register reg; @@ -2323,11 +2297,6 @@ struct spirv_compiler
enum vkd3d_shader_type shader_type;
- 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; @@ -2409,8 +2378,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); @@ -7575,208 +7542,6 @@ 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) -{ - 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; - } - - return &compiler->control_flow_info[compiler->control_flow_depth++]; -} - -static void spirv_compiler_pop_control_flow_level(struct spirv_compiler *compiler) -{ - struct vkd3d_control_flow_info *cf_info; - - assert(compiler->control_flow_depth); - - cf_info = &compiler->control_flow_info[--compiler->control_flow_depth]; - memset(cf_info, 0, sizeof(*cf_info)); -} - -static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_cf_construct( - struct spirv_compiler *compiler) -{ - int depth; - - for (depth = compiler->control_flow_depth - 1; depth >= 0; --depth) - { - if (compiler->control_flow_info[depth].current_block == VKD3D_BLOCK_SWITCH) - return &compiler->control_flow_info[depth]; - } - - return NULL; -} - -static void spirv_compiler_emit_label(struct spirv_compiler *compiler, - const struct vkd3d_shader_instruction *instruction); - -static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *compiler, - const struct vkd3d_shader_instruction *instruction) -{ - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - const struct vkd3d_shader_src_param *src = instruction->src; - struct vkd3d_control_flow_info *cf_info; - uint32_t merge_block_id, val_id; - - cf_info = compiler->control_flow_depth - ? &compiler->control_flow_info[compiler->control_flow_depth - 1] : NULL; - - switch (instruction->handler_idx) - { - case VKD3DSIH_LABEL: - if (cf_info) - cf_info->inside_block = true; - spirv_compiler_emit_label(compiler, instruction); - 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_u32; - - 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_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_RET: - spirv_compiler_emit_return(compiler, instruction); - - if (cf_info) - cf_info->inside_block = false; - break; - - default: - ERR("Unexpected instruction %#x.\n", instruction->handler_idx); - break; - } - - return VKD3D_OK; -} - static bool spirv_compiler_init_blocks(struct spirv_compiler *compiler, unsigned int block_count) { compiler->block_count = block_count; @@ -7855,6 +7620,47 @@ static void spirv_compiler_emit_branch(struct spirv_compiler *compiler, spirv_compiler_get_label_id(compiler, src[2].reg.idx[0].offset)); }
+static void spirv_compiler_emit_switch(struct spirv_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) +{ + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + const struct vkd3d_shader_src_param *src = instruction->src; + uint32_t val_id, default_id; + unsigned int i, word_count; + uint32_t *cases; + + if (!vkd3d_swizzle_is_scalar(src[0].swizzle)) + { + WARN("Unexpected src swizzle %#x.\n", src[0].swizzle); + spirv_compiler_warning(compiler, VKD3D_SHADER_WARNING_SPV_INVALID_SWIZZLE, + "The swizzle for a switch value is not scalar."); + } + + word_count = instruction->src_count - 3; + if (!(cases = vkd3d_calloc(word_count, sizeof(*cases)))) + { + spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_OUT_OF_MEMORY, + "Failed to allocate %u words for switch cases.", word_count); + return; + } + + val_id = spirv_compiler_emit_load_src(compiler, &src[0], VKD3DSP_WRITEMASK_0); + default_id = spirv_compiler_get_label_id(compiler, src[1].reg.idx[0].offset); + /* No instructions may occur between the merge and the switch. */ + spirv_compiler_emit_merge(compiler, src[2].reg.idx[0].offset, 0); + + src = &src[3]; + for (i = 0; i < word_count; i += 2) + { + cases[i] = src[i].reg.u.immconst_u32[0]; + cases[i + 1] = spirv_compiler_get_label_id(compiler, src[i + 1].reg.idx[0].offset); + } + + vkd3d_spirv_build_op_switch(builder, val_id, default_id, cases, word_count / 2u); + + vkd3d_free(cases); +} + static void spirv_compiler_emit_deriv_instruction(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { @@ -9599,19 +9405,8 @@ 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_ENDLOOP: - case VKD3DSIH_ENDSWITCH: - case VKD3DSIH_LABEL: - case VKD3DSIH_LOOP: case VKD3DSIH_RET: - case VKD3DSIH_SWITCH: - ret = spirv_compiler_emit_control_flow_instruction(compiler, instruction); + spirv_compiler_emit_return(compiler, instruction); break; case VKD3DSIH_RETP: spirv_compiler_emit_retc(compiler, instruction); @@ -9620,9 +9415,15 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_TEXKILL: 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_MONOLITHIC: + spirv_compiler_emit_switch(compiler, instruction); + break; case VKD3DSIH_DSX: case VKD3DSIH_DSX_COARSE: case VKD3DSIH_DSX_FINE: diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 30672950b..9c85dfc38 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -472,6 +472,7 @@ enum vkd3d_shader_opcode VKD3DSIH_SUB, VKD3DSIH_SWAPC, VKD3DSIH_SWITCH, + VKD3DSIH_SWITCH_MONOLITHIC, VKD3DSIH_SYNC, VKD3DSIH_TEX, VKD3DSIH_TEXBEM,
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxbc.c | 5 ++ libs/vkd3d-shader/ir.c | 58 ++++++++++++++++++++++++ libs/vkd3d-shader/spirv.c | 8 ++++ libs/vkd3d-shader/vkd3d_shader_private.h | 3 ++ 4 files changed, 74 insertions(+)
diff --git a/libs/vkd3d-shader/dxbc.c b/libs/vkd3d-shader/dxbc.c index 63deaaad2..22e29387f 100644 --- a/libs/vkd3d-shader/dxbc.c +++ b/libs/vkd3d-shader/dxbc.c @@ -551,9 +551,14 @@ static int shdr_handler(const struct vkd3d_shader_dxbc_section_desc *section,
void free_shader_desc(struct vkd3d_shader_desc *desc) { + size_t i; + shader_signature_cleanup(&desc->input_signature); shader_signature_cleanup(&desc->output_signature); shader_signature_cleanup(&desc->patch_constant_signature); + for (i = 0; i < desc->block_name_count; ++i) + vkd3d_free((void *)desc->block_names[i]); + vkd3d_free(desc->block_names); }
int shader_extract_from_dxbc(const struct vkd3d_shader_code *dxbc, diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index e6758a638..cddfd0525 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -1511,6 +1511,7 @@ static enum vkd3d_result normalise_combined_samplers(struct vkd3d_shader_parser struct cf_flattener_if_info { struct vkd3d_shader_src_param *false_param; + unsigned int id; uint32_t merge_block_id; unsigned int else_block_id; }; @@ -1532,6 +1533,7 @@ struct cf_flattener_switch_info { size_t ins_location; const struct vkd3d_shader_src_param *condition; + unsigned int id; unsigned int merge_block_id; unsigned int default_block_id; struct cf_flattener_switch_case *cases; @@ -1569,6 +1571,13 @@ struct cf_flattener size_t instruction_count;
unsigned int block_id; + const char **block_names; + size_t block_name_capacity; + size_t block_name_count; + + unsigned int branch_id; + unsigned int loop_id; + unsigned int switch_id;
unsigned int control_flow_depth; struct cf_flattener_info *control_flow_info; @@ -1751,6 +1760,31 @@ static struct cf_flattener_info *cf_flattener_find_innermost_breakable_cf_constr return NULL; }
+static void VKD3D_PRINTF_FUNC(3, 4) cf_flattener_create_block_name(struct cf_flattener *flattener, + unsigned int block_id, const char *fmt, ...) +{ + struct vkd3d_string_buffer buffer; + size_t block_name_count; + va_list args; + + --block_id; + + block_name_count = max(flattener->block_name_count, block_id + 1); + if (!vkd3d_array_reserve((void **)&flattener->block_names, &flattener->block_name_capacity, + block_name_count, sizeof(*flattener->block_names))) + return; + memset(&flattener->block_names[flattener->block_name_count], 0, + (block_name_count - flattener->block_name_count) * sizeof(*flattener->block_names)); + flattener->block_name_count = block_name_count; + + vkd3d_string_buffer_init(&buffer); + va_start(args, fmt); + vkd3d_string_buffer_vprintf(&buffer, fmt, args); + va_end(args); + + flattener->block_names[block_id] = buffer.buffer; +} + static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flattener *flattener) { struct vkd3d_shader_parser *parser = flattener->parser; @@ -1797,10 +1831,15 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte
cf_flattener_emit_label(flattener, true_block_id);
+ cf_info->u.if_.id = flattener->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; + + cf_flattener_create_block_name(flattener, merge_block_id, "branch%u_merge", flattener->branch_id); + cf_flattener_create_block_name(flattener, true_block_id, "branch%u_true", flattener->branch_id); + ++flattener->branch_id; break;
case VKD3DSIH_ELSE: @@ -1810,6 +1849,8 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte cf_info->u.if_.else_block_id = cf_flattener_alloc_block_id(flattener); cf_info->u.if_.false_param->reg.idx[0].offset = cf_info->u.if_.else_block_id;
+ cf_flattener_create_block_name(flattener, + cf_info->u.if_.else_block_id, "branch%u_false", cf_info->u.if_.id); cf_flattener_emit_label(flattener, cf_info->u.if_.else_block_id);
cf_info->inside_block = true; @@ -1845,6 +1886,12 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte cf_info->u.loop.merge_block_id = merge_block_id; cf_info->current_block = VKD3D_BLOCK_LOOP; cf_info->inside_block = true; + + cf_flattener_create_block_name(flattener, loop_header_block_id, "loop%u_header", flattener->loop_id); + cf_flattener_create_block_name(flattener, loop_body_block_id, "loop%u_body", flattener->loop_id); + cf_flattener_create_block_name(flattener, continue_block_id, "loop%u_continue", flattener->loop_id); + cf_flattener_create_block_name(flattener, merge_block_id, "loop%u_merge", flattener->loop_id); + ++flattener->loop_id; break;
case VKD3DSIH_ENDLOOP: @@ -1872,6 +1919,7 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte vsir_instruction_init(dst_ins, &instruction->location, VKD3DSIH_SWITCH_MONOLITHIC); ++flattener->instruction_count;
+ cf_info->u.switch_.id = flattener->switch_id; cf_info->u.switch_.merge_block_id = merge_block_id; cf_info->u.switch_.cases = NULL; cf_info->u.switch_.cases_size = 0; @@ -1880,6 +1928,9 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte cf_info->inside_block = false; cf_info->current_block = VKD3D_BLOCK_SWITCH;
+ cf_flattener_create_block_name(flattener, merge_block_id, "switch%u_merge", flattener->switch_id); + ++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; @@ -1946,6 +1997,7 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte ++cf_info->u.switch_.cases_count;
cf_flattener_emit_label(flattener, label_id); + cf_flattener_create_block_name(flattener, label_id, "switch%u_case%u", cf_info->u.switch_.id, value); cf_info->inside_block = true; break; } @@ -1956,6 +2008,9 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte 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_flattener_create_block_name(flattener, cf_info->u.switch_.default_block_id, + "switch%u_default", cf_info->u.switch_.id); cf_info->inside_block = true; break;
@@ -2078,6 +2133,9 @@ static enum vkd3d_result flatten_control_flow_constructs(struct vkd3d_shader_par }
vkd3d_free(flattener.control_flow_info); + /* Simpler to always free these in free_shader_desc(). */ + parser->shader_desc.block_names = flattener.block_names; + parser->shader_desc.block_name_count = flattener.block_name_count;
return result; } diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index fb2028999..40f6584f9 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2349,6 +2349,8 @@ struct spirv_compiler
uint32_t *block_label_ids; unsigned int block_count; + const char **block_names; + size_t block_name_count; };
static bool is_in_default_phase(const struct spirv_compiler *compiler) @@ -7562,6 +7564,10 @@ static void spirv_compiler_emit_label(struct spirv_compiler *compiler,
label_id = spirv_compiler_get_label_id(compiler, block_id); vkd3d_spirv_build_op_label(builder, label_id); + + --block_id; + if (block_id < compiler->block_name_count && compiler->block_names[block_id]) + vkd3d_spirv_build_op_name(builder, label_id, compiler->block_names[block_id]); }
static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, @@ -9657,6 +9663,8 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, memset(&shader_desc->output_signature, 0, sizeof(shader_desc->output_signature)); memset(&shader_desc->patch_constant_signature, 0, sizeof(shader_desc->patch_constant_signature)); compiler->use_vocp = parser->shader_desc.use_vocp; + compiler->block_names = parser->shader_desc.block_names; + compiler->block_name_count = parser->shader_desc.block_name_count;
compiler->input_control_point_count = shader_desc->input_control_point_count; compiler->output_control_point_count = shader_desc->output_control_point_count; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 9c85dfc38..81c5a0ca7 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1025,6 +1025,9 @@ struct vkd3d_shader_desc } flat_constant_count[3];
bool use_vocp; + + const char **block_names; + size_t block_name_count; };
struct vkd3d_shader_register_semantic
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 14 ++++++++++ libs/vkd3d-shader/ir.c | 33 +++++++++++++++++++++--- libs/vkd3d-shader/spirv.c | 33 +++++++++++++++++++----- libs/vkd3d-shader/vkd3d_shader_private.h | 2 ++ 4 files changed, 71 insertions(+), 11 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index c744dfced..3b667833e 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -4868,6 +4868,18 @@ static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const st return VKD3D_OK; }
+static void sm6_parser_emit_label(struct sm6_parser *sm6, unsigned int label_id) +{ + struct vkd3d_shader_src_param *src_param; + struct vkd3d_shader_instruction *ins; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_LABEL); + + if (!(src_param = instruction_src_params_alloc(ins, 1, sm6))) + return; + vsir_src_param_init_label(src_param, label_id); +} + static bool sm6_parser_allocate_named_metadata(struct sm6_parser *sm6) { struct dxil_block *block; @@ -6216,6 +6228,7 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t }
sm6->p.shader_desc.ssa_count = sm6->ssa_next_id; + sm6->p.shader_desc.block_count = 1;
if (!(fn = sm6_parser_get_function(sm6, sm6->entry_point))) { @@ -6226,6 +6239,7 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t }
assert(sm6->function_count == 1); + sm6_parser_emit_label(sm6, 1); if (!sm6_block_emit_instructions(fn->blocks[0], sm6)) { vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index cddfd0525..6eb25ef69 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -320,7 +320,7 @@ static void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d param->modifiers = VKD3DSPSM_NONE; }
-static void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id) +void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id) { vsir_src_param_init(param, VKD3DSPR_LABEL, VKD3D_DATA_UINT, 1); param->reg.dimension = VSIR_DIMENSION_NONE; @@ -1574,6 +1574,7 @@ struct cf_flattener const char **block_names; size_t block_name_capacity; size_t block_name_count; + bool after_declarations_section;
unsigned int branch_id; unsigned int loop_id; @@ -1785,18 +1786,28 @@ static void VKD3D_PRINTF_FUNC(3, 4) cf_flattener_create_block_name(struct cf_fla flattener->block_names[block_id] = buffer.buffer; }
+static bool vsir_instruction_is_dcl(const struct vkd3d_shader_instruction *instruction) +{ + enum vkd3d_shader_opcode handler_idx = instruction->handler_idx; + /* Indexable temps must be emitted after the initial label. All other declarations + * are emitted earlier to make the trace output appear more logical. */ + return (VKD3DSIH_DCL <= handler_idx && handler_idx <= VKD3DSIH_DCL_VERTICES_OUT + && handler_idx != VKD3DSIH_DCL_INDEXABLE_TEMP) || handler_idx == VKD3DSIH_HS_DECLS; +} + static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flattener *flattener) { struct vkd3d_shader_parser *parser = flattener->parser; struct vkd3d_shader_instruction_array *instructions; struct vkd3d_shader_instruction *dst_ins; - bool main_block_open; + bool main_block_open, is_hull_shader; size_t i;
instructions = &parser->instructions; - main_block_open = parser->shader_version.type != VKD3D_SHADER_TYPE_HULL; + is_hull_shader = parser->shader_version.type == VKD3D_SHADER_TYPE_HULL; + main_block_open = !is_hull_shader;
- if (!cf_flattener_require_space(flattener, instructions->count)) + if (!cf_flattener_require_space(flattener, instructions->count + 1)) return VKD3D_ERROR_OUT_OF_MEMORY;
for (i = 0; i < instructions->count; ++i) @@ -1808,11 +1819,25 @@ static enum vkd3d_result cf_flattener_iterate_instruction_array(struct cf_flatte
flattener->location = instruction->location;
+ if (!is_hull_shader && !flattener->after_declarations_section && !vsir_instruction_is_dcl(instruction)) + { + flattener->after_declarations_section = true; + cf_flattener_emit_label(flattener, cf_flattener_alloc_block_id(flattener)); + } + cf_info = flattener->control_flow_depth ? &flattener->control_flow_info[flattener->control_flow_depth - 1] : NULL;
switch (instruction->handler_idx) { + case VKD3DSIH_HS_CONTROL_POINT_PHASE: + case VKD3DSIH_HS_FORK_PHASE: + case VKD3DSIH_HS_JOIN_PHASE: + if (!cf_flattener_copy_instruction(flattener, instruction)) + return VKD3D_ERROR_OUT_OF_MEMORY; + cf_flattener_emit_label(flattener, cf_flattener_alloc_block_id(flattener)); + break; + case VKD3DSIH_LABEL: vkd3d_shader_parser_error(parser, VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED, "Aborting due to not yet implemented feature: Label instruction."); diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 40f6584f9..2501c325c 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -1862,8 +1862,6 @@ static void vkd3d_spirv_builder_begin_main_function(struct vkd3d_spirv_builder *
vkd3d_spirv_build_op_function(builder, void_id, builder->main_function_id, SpvFunctionControlMaskNone, function_type_id); - vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder)); - builder->main_function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); }
static void vkd3d_spirv_builder_free(struct vkd3d_spirv_builder *builder) @@ -2306,6 +2304,7 @@ struct spirv_compiler const struct vkd3d_shader_spirv_target_info *spirv_target_info;
bool after_declarations_section; + bool io_declared; struct shader_signature input_signature; struct shader_signature output_signature; struct shader_signature patch_constant_signature; @@ -2370,6 +2369,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 void spirv_compiler_emit_io_declarations(struct spirv_compiler *compiler);
static const char *spirv_compiler_get_entry_point_name(const struct spirv_compiler *compiler) { @@ -6482,15 +6482,14 @@ static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler, vkd3d_spirv_build_op_function(builder, void_id, function_id, SpvFunctionControlMaskNone, function_type_id);
- vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder)); - compiler->phase = instruction->handler_idx; spirv_compiler_emit_shader_phase_name(compiler, function_id, NULL);
phase = (instruction->handler_idx == VKD3DSIH_HS_CONTROL_POINT_PHASE) ? &compiler->control_point_phase : &compiler->patch_constant_phase; phase->function_id = function_id; - phase->function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); + /* The insertion location must be set after the label is emitted. */ + phase->function_location = 0;
if (instruction->handler_idx == VKD3DSIH_HS_CONTROL_POINT_PHASE) compiler->emit_default_control_point_phase = instruction->flags; @@ -6606,6 +6605,7 @@ static void spirv_compiler_emit_hull_shader_main(struct spirv_compiler *compiler memset(compiler->descriptor_offset_ids, 0, compiler->offset_info.descriptor_table_count * sizeof(*compiler->descriptor_offset_ids)); vkd3d_spirv_builder_begin_main_function(builder); + vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder));
void_id = vkd3d_spirv_get_op_type_void(builder);
@@ -7568,6 +7568,27 @@ static void spirv_compiler_emit_label(struct spirv_compiler *compiler, --block_id; if (block_id < compiler->block_name_count && compiler->block_names[block_id]) vkd3d_spirv_build_op_name(builder, label_id, compiler->block_names[block_id]); + + /* Insertion locations must point immediately after the function's initial label. */ + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) + { + struct vkd3d_shader_phase *phase = (compiler->phase == VKD3DSIH_HS_CONTROL_POINT_PHASE) + ? &compiler->control_point_phase : &compiler->patch_constant_phase; + if (!phase->function_location) + phase->function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); + } + else if (!builder->main_function_location) + { + builder->main_function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); + } + + /* I/O declarations can result in emission of fixups, which must occur after the initial label. + * TODO: emit fixups in the IR? */ + if (!compiler->io_declared) + { + spirv_compiler_emit_io_declarations(compiler); + compiler->io_declared = true; + } }
static void spirv_compiler_emit_merge(struct spirv_compiler *compiler, @@ -9672,8 +9693,6 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) spirv_compiler_emit_shader_signature_outputs(compiler);
- spirv_compiler_emit_io_declarations(compiler); - for (i = 0; i < instructions.count && result >= 0; ++i) { compiler->location.line = i + 1; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 81c5a0ca7..b42090472 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -884,6 +884,8 @@ struct vkd3d_shader_src_param enum vkd3d_shader_src_modifier modifiers; };
+void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id); + struct vkd3d_shader_index_range { struct vkd3d_shader_dst_param dst;
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 3b667833e..b786b5152 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -472,11 +472,23 @@ struct sm6_symbol const char *name; };
+enum sm6_block_terminator_type +{ + TERMINATOR_RET, +}; + +struct sm6_block_terminator +{ + enum sm6_block_terminator_type type; +}; + struct sm6_block { struct vkd3d_shader_instruction *instructions; size_t instruction_capacity; size_t instruction_count; + + struct sm6_block_terminator terminator; };
struct sm6_function @@ -4296,6 +4308,8 @@ static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record if (record->operand_count) FIXME("Non-void return is not implemented.\n");
+ code_block->terminator.type = TERMINATOR_RET; + ins->handler_idx = VKD3DSIH_NOP; }
@@ -4796,6 +4810,21 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const return VKD3D_OK; }
+static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) +{ + switch (block->terminator.type) + { + case TERMINATOR_RET: + { + sm6_parser_add_instruction(sm6, VKD3DSIH_RET); + break; + } + + default: + vkd3d_unreachable(); + } +} + static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_parser *sm6) { struct vkd3d_shader_instruction *ins = sm6_parser_require_space(sm6, block->instruction_count + 1); @@ -4806,7 +4835,7 @@ static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_pars memcpy(ins, block->instructions, block->instruction_count * sizeof(*block->instructions)); sm6->p.instructions.count += block->instruction_count;
- sm6_parser_add_instruction(sm6, VKD3DSIH_RET); + sm6_block_emit_terminator(block, sm6);
return true; }
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 167 +++++++++++++++++++++++++++++++-------- 1 file changed, 133 insertions(+), 34 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index b786b5152..2236cbfbf 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -474,12 +474,14 @@ struct sm6_symbol
enum sm6_block_terminator_type { + TERMINATOR_UNCOND_BR, TERMINATOR_RET, };
struct sm6_block_terminator { enum sm6_block_terminator_type type; + const struct sm6_block *true_block; };
struct sm6_block @@ -488,6 +490,9 @@ struct sm6_block size_t instruction_capacity; size_t instruction_count;
+ /* A nonzero id. */ + unsigned int id; + struct sm6_block_terminator terminator; };
@@ -495,7 +500,8 @@ struct sm6_function { const struct sm6_value *declaration;
- struct sm6_block *blocks[1]; + struct sm6_block **blocks; + size_t block_capacity; size_t block_count;
size_t value_count; @@ -3305,6 +3311,45 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, const struct dxil_reco } }
+static const struct sm6_block *sm6_function_get_block(const struct sm6_function *function, uint64_t index, + struct sm6_parser *sm6) +{ + if (index >= function->block_count) + { + WARN("Invalid code block index %#"PRIx64".\n", index); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid code block index %#"PRIx64" for a control flow instruction.", index); + return NULL; + } + return function->blocks[index]; +} + +static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) +{ + if (record->operand_count != 1 && record->operand_count < 3) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for a branch instruction.", record->operand_count); + return; + } + + if (record->operand_count == 1) + { + code_block->terminator.type = TERMINATOR_UNCOND_BR; + code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); + } + else + { + FIXME("Conditional branch is not implemented.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Conditional branch is not implemented."); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + static enum vkd3d_shader_opcode map_dx_unary_op(enum dx_intrinsic_opcode op) { switch (op) @@ -4654,6 +4699,31 @@ static void metadata_attachment_record_apply(const struct dxil_record *record, e } }
+static bool sm6_function_blocks_reserve(struct sm6_function *function, unsigned int reserve) +{ + if (!vkd3d_array_reserve((void **)&function->blocks, &function->block_capacity, + reserve, sizeof(*function->blocks))) + { + ERR("Failed to allocate code block array.\n"); + return false; + } + return true; +} + +static struct sm6_block *sm6_function_create_block(struct sm6_function *function) +{ + struct sm6_block *block; + + if (!(block = sm6_block_create())) + return NULL; + + function->blocks[function->block_count++] = block; + /* Set the id to the array index + 1. */ + block->id = function->block_count; + + return block; +} + static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, struct sm6_function *function) { @@ -4693,16 +4763,18 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const WARN("Function contains no blocks.\n"); return VKD3D_ERROR_INVALID_SHADER; } - if (block_count > 1) - { - FIXME("Branched shaders are not supported yet.\n"); - return VKD3D_ERROR_INVALID_SHADER; - }
- if (!(function->blocks[0] = sm6_block_create())) - { - ERR("Failed to allocate code block.\n"); + if (!sm6_function_blocks_reserve(function, block_count)) return VKD3D_ERROR_OUT_OF_MEMORY; + + /* Pre-allocate all blocks to simplify instruction parsing. */ + for (i = 0; i < block_count; ++i) + { + if (!sm6_function_create_block(function)) + { + ERR("Failed to allocate code block.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } } function->block_count = block_count; code_block = function->blocks[0]; @@ -4721,10 +4793,9 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const return VKD3D_ERROR_INVALID_SHADER; }
- /* block->record_count - 1 is the instruction count, but some instructions - * can emit >1 IR instruction, so extra may be used. */ + /* Some instructions can emit >1 IR instruction, so extra may be used. */ if (!vkd3d_array_reserve((void **)&code_block->instructions, &code_block->instruction_capacity, - max(code_block->instruction_count + 1, block->record_count), sizeof(*code_block->instructions))) + code_block->instruction_count + 1, sizeof(*code_block->instructions))) { ERR("Failed to allocate instructions.\n"); return VKD3D_ERROR_OUT_OF_MEMORY; @@ -4747,6 +4818,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_BINOP: sm6_parser_emit_binop(sm6, record, ins, dst); break; + case FUNC_CODE_INST_BR: + sm6_parser_emit_br(sm6, record, function, code_block, ins); + is_terminator = true; + break; case FUNC_CODE_INST_CALL: sm6_parser_emit_call(sm6, record, code_block, ins, dst); break; @@ -4812,8 +4887,22 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const
static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) { + struct vkd3d_shader_src_param *src_params; + struct vkd3d_shader_instruction *ins; + switch (block->terminator.type) { + case TERMINATOR_UNCOND_BR: + { + if (!block->terminator.true_block) + return; + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); + if (!(src_params = instruction_src_params_alloc(ins, 1, sm6))) + return; + vsir_src_param_init_label(&src_params[0], block->terminator.true_block->id); + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -4825,21 +4914,6 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ } }
-static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_parser *sm6) -{ - struct vkd3d_shader_instruction *ins = sm6_parser_require_space(sm6, block->instruction_count + 1); - - if (!ins) - return false; - - memcpy(ins, block->instructions, block->instruction_count * sizeof(*block->instructions)); - sm6->p.instructions.count += block->instruction_count; - - sm6_block_emit_terminator(block, sm6); - - return true; -} - static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int level) { @@ -4909,6 +4983,35 @@ static void sm6_parser_emit_label(struct sm6_parser *sm6, unsigned int label_id) vsir_src_param_init_label(src_param, label_id); }
+static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *function, struct sm6_parser *sm6) +{ + unsigned int i; + + sm6->p.shader_desc.block_count = function->block_count; + + for (i = 0; i < function->block_count; ++i) + { + const struct sm6_block *block = function->blocks[i]; + + /* Space for the label and terminator. */ + if (!sm6_parser_require_space(sm6, block->instruction_count + 2)) + { + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory emitting shader instructions."); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + sm6_parser_emit_label(sm6, block->id); + + memcpy(&sm6->p.instructions.elements[sm6->p.instructions.count], block->instructions, + block->instruction_count * sizeof(*block->instructions)); + sm6->p.instructions.count += block->instruction_count; + + sm6_block_emit_terminator(block, sm6); + } + + return VKD3D_OK; +} + static bool sm6_parser_allocate_named_metadata(struct sm6_parser *sm6) { struct dxil_block *block; @@ -5972,6 +6075,7 @@ static void sm6_functions_cleanup(struct sm6_function *functions, size_t count) { for (j = 0; j < functions[i].block_count; ++j) sm6_block_destroy(functions[i].blocks[j]); + vkd3d_free(functions[i].blocks); } vkd3d_free(functions); } @@ -6268,13 +6372,8 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t }
assert(sm6->function_count == 1); - sm6_parser_emit_label(sm6, 1); - if (!sm6_block_emit_instructions(fn->blocks[0], sm6)) - { - vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, - "Out of memory emitting shader instructions."); - return VKD3D_ERROR_OUT_OF_MEMORY; - } + if ((ret = sm6_function_emit_blocks(fn, sm6)) < 0) + return ret;
dxil_block_destroy(&sm6->root_block);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 2236cbfbf..ab60fce5e 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -475,13 +475,16 @@ struct sm6_symbol enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, + TERMINATOR_COND_BR, TERMINATOR_RET, };
struct sm6_block_terminator { + struct vkd3d_shader_register conditional_reg; enum sm6_block_terminator_type type; const struct sm6_block *true_block; + const struct sm6_block *false_block; };
struct sm6_block @@ -3327,6 +3330,9 @@ static const struct sm6_block *sm6_function_get_block(const struct sm6_function static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record *record, struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) { + const struct sm6_value *value; + unsigned int i = 2; + if (record->operand_count != 1 && record->operand_count < 3) { WARN("Invalid operand count %u.\n", record->operand_count); @@ -3342,9 +3348,22 @@ static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record } else { - FIXME("Conditional branch is not implemented.\n"); - vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, - "Conditional branch is not implemented."); + if (!sm6->bool_type) + { + WARN("Bool type not found.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE, + "Module does not define a boolean type for conditions."); + return; + } + if (!(value = sm6_parser_get_value_by_ref(sm6, record, sm6->bool_type, &i)) + || !sm6_value_validate_is_bool(value, sm6)) + return; + dxil_record_validate_operand_max_count(record, i, sm6); + + code_block->terminator.type = TERMINATOR_COND_BR; + code_block->terminator.conditional_reg = value->u.reg; + code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); + code_block->terminator.false_block = sm6_function_get_block(function, record->operands[1], sm6); }
ins->handler_idx = VKD3DSIH_NOP; @@ -4903,6 +4922,20 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ break; }
+ case TERMINATOR_COND_BR: + { + if (!block->terminator.true_block || !block->terminator.false_block) + return; + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); + if (!(src_params = instruction_src_params_alloc(ins, 3, sm6))) + return; + src_param_init(&src_params[0]); + src_params[0].reg = block->terminator.conditional_reg; + vsir_src_param_init_label(&src_params[1], block->terminator.true_block->id); + vsir_src_param_init_label(&src_params[2], block->terminator.false_block->id); + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 1 + libs/vkd3d-shader/dxil.c | 209 ++++++++++++++++++++++- libs/vkd3d-shader/spirv.c | 7 +- libs/vkd3d-shader/vkd3d_shader_private.h | 6 + 4 files changed, 218 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 120eca06b..fe3133a1a 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -242,6 +242,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_NRM ] = "nrm", [VKD3DSIH_OR ] = "or", [VKD3DSIH_PHASE ] = "phase", + [VKD3DSIH_PHI ] = "phi", [VKD3DSIH_POW ] = "pow", [VKD3DSIH_RCP ] = "rcp", [VKD3DSIH_REP ] = "rep", diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index ab60fce5e..8d43e6364 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -472,6 +472,20 @@ struct sm6_symbol const char *name; };
+struct incoming_value +{ + const struct sm6_block *block; + struct vkd3d_shader_register reg; +}; + +struct sm6_phi +{ + struct vkd3d_shader_register reg; + struct incoming_value *incoming; + size_t incoming_capacity; + size_t incoming_count; +}; + enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, @@ -496,6 +510,10 @@ struct sm6_block /* A nonzero id. */ unsigned int id;
+ struct sm6_phi *phi; + size_t phi_capacity; + size_t phi_count; + struct sm6_block_terminator terminator; };
@@ -1968,6 +1986,11 @@ static bool sm6_value_is_icb(const struct sm6_value *value) return value->value_type == VALUE_TYPE_ICB; }
+static bool sm6_value_is_ssa(const struct sm6_value *value) +{ + return sm6_value_is_register(value) && register_is_ssa(&value->u.reg); +} + static inline unsigned int sm6_value_get_constant_uint(const struct sm6_value *value) { if (!sm6_value_is_constant(value)) @@ -3052,6 +3075,26 @@ static struct sm6_block *sm6_block_create() return block; }
+static struct sm6_phi *sm6_block_phi_require_space(struct sm6_block *block, struct sm6_parser *sm6) +{ + struct sm6_phi *phi; + + if (!vkd3d_array_reserve((void **)&block->phi, &block->phi_capacity, block->phi_count + 1, sizeof(*block->phi))) + { + ERR("Failed to allocate phi array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a phi instruction."); + return NULL; + } + phi = &block->phi[block->phi_count++]; + + phi->incoming = NULL; + phi->incoming_capacity = 0; + phi->incoming_count = 0; + + return phi; +} + static void sm6_parser_emit_alloca(struct sm6_parser *sm6, const struct dxil_record *record, struct vkd3d_shader_instruction *ins, struct sm6_value *dst) { @@ -4363,6 +4406,72 @@ static void sm6_parser_emit_load(struct sm6_parser *sm6, const struct dxil_recor instruction_dst_param_init_ssa_scalar(ins, sm6); }
+static void sm6_parser_emit_phi(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins, + struct sm6_value *dst) +{ + const struct sm6_type *type; + struct sm6_phi *phi; + unsigned int i, j; + uint64_t src_idx; + + if (!(record->operand_count & 1)) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for phi instruction.", record->operand_count); + return; + } + + if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) + return; + if (!sm6_type_is_numeric(type)) + { + /* dxc doesn't seem to use buffer/resource read return types here. */ + FIXME("Only scalar numeric types are supported.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Result type class %u of a phi instruction is not scalar numeric.", type->class); + return; + } + + dst->type = type; + register_init_ssa_scalar(&dst->u.reg, type, sm6); + + if (!(phi = sm6_block_phi_require_space(code_block, sm6))) + return; + phi->reg = dst->u.reg; + phi->incoming_count = record->operand_count / 2u; + + if (!vkd3d_array_reserve((void **)&phi->incoming, &phi->incoming_capacity, phi->incoming_count, + sizeof(*phi->incoming))) + { + ERR("Failed to allocate phi incoming array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a phi incoming array."); + return; + } + + for (i = 1; i < record->operand_count; i += 2) + { + src_idx = sm6->value_count - decode_rotated_signed_value(record->operands[i]); + /* May be a forward reference. */ + if (src_idx >= sm6->cur_max_value) + { + FIXME("Invalid value index %"PRIu64".\n", src_idx); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Invalid value index %"PRIu64" for a phi incoming value.", src_idx); + return; + } + + j = i / 2u; + /* Store the value index in the register for later resolution. */ + phi->incoming[j].reg.idx[0].offset = src_idx; + phi->incoming[j].block = sm6_function_get_block(function, record->operands[i + 1], sm6); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record *record, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) { @@ -4743,6 +4852,45 @@ static struct sm6_block *sm6_function_create_block(struct sm6_function *function return block; }
+static enum vkd3d_result sm6_function_resolve_phi_incomings(const struct sm6_function *function, + struct sm6_parser *sm6) +{ + const struct sm6_block *block; + size_t i, j, block_idx; + + for (block_idx = 0; block_idx < function->block_count; ++block_idx) + { + block = function->blocks[block_idx]; + + for (i = 0; i < block->phi_count; ++i) + { + struct sm6_phi *phi = &block->phi[i]; + const struct sm6_value *src; + + for (j = 0; j < phi->incoming_count; ++j) + { + src = &sm6->values[phi->incoming[j].reg.idx[0].offset]; + if (!sm6_value_is_constant(src) && !sm6_value_is_undef(src) && !sm6_value_is_ssa(src)) + { + FIXME("PHI incoming value is not a constant or SSA register.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "A PHI incoming value is not a constant or SSA register."); + return VKD3D_ERROR_INVALID_SHADER; + } + if (src->u.reg.data_type != phi->reg.data_type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a phi incoming value does not match the result type."); + } + phi->incoming[j].reg = src->u.reg; + } + } + } + + return VKD3D_OK; +} + static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, struct sm6_function *function) { @@ -4859,6 +5007,9 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_LOAD: sm6_parser_emit_load(sm6, record, ins, dst); break; + case FUNC_CODE_INST_PHI: + sm6_parser_emit_phi(sm6, record, function, code_block, ins, dst); + break; case FUNC_CODE_INST_RET: sm6_parser_emit_ret(sm6, record, code_block, ins); is_terminator = true; @@ -4901,7 +5052,7 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const return VKD3D_ERROR_INVALID_SHADER; }
- return VKD3D_OK; + return sm6_function_resolve_phi_incomings(function, sm6); }
static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) @@ -4947,6 +5098,46 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ } }
+static void sm6_block_emit_phi(const struct sm6_block *block, struct sm6_parser *sm6) +{ + struct vkd3d_shader_instruction *ins; + unsigned int i, j, incoming_count; + const struct sm6_phi *src_phi; + + sm6_parser_require_space(sm6, block->phi_count); + + for (i = 0; i < block->phi_count; ++i) + { + struct vkd3d_shader_src_param *src_params; + struct vkd3d_shader_dst_param *dst_param; + + src_phi = &block->phi[i]; + incoming_count = src_phi->incoming_count; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_PHI); + if (!(src_params = instruction_src_params_alloc(ins, incoming_count * 2u, sm6))) + return; + if (!(dst_param = instruction_dst_params_alloc(ins, 1, sm6))) + return; + + for (j = 0; j < incoming_count; ++j) + { + const struct sm6_block *incoming_block = src_phi->incoming[j].block; + unsigned int index = j * 2; + + src_param_init(&src_params[index]); + src_params[index].reg = src_phi->incoming[j].reg; + if (incoming_block) + vsir_src_param_init_label(&src_params[index + 1], incoming_block->id); + else + assert(sm6->p.failed); + } + + dst_param_init(dst_param); + dst_param->reg = src_phi->reg; + } +} + static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int level) { @@ -5026,14 +5217,15 @@ static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *fun { const struct sm6_block *block = function->blocks[i];
- /* Space for the label and terminator. */ - if (!sm6_parser_require_space(sm6, block->instruction_count + 2)) + /* Space for a branch, label and terminator. */ + if (!sm6_parser_require_space(sm6, block->instruction_count + block->phi_count + 3)) { vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, "Out of memory emitting shader instructions."); return VKD3D_ERROR_OUT_OF_MEMORY; } sm6_parser_emit_label(sm6, block->id); + sm6_block_emit_phi(block, sm6);
memcpy(&sm6->p.instructions.elements[sm6->p.instructions.count], block->instructions, block->instruction_count * sizeof(*block->instructions)); @@ -6094,9 +6286,19 @@ static void sm6_symtab_cleanup(struct sm6_symbol *symbols, size_t count) vkd3d_free(symbols); }
+static void sm6_phi_destroy(struct sm6_phi *phi) +{ + vkd3d_free(phi->incoming); +} + static void sm6_block_destroy(struct sm6_block *block) { + unsigned int i; + vkd3d_free(block->instructions); + for (i = 0; i < block->phi_count; ++i) + sm6_phi_destroy(&block->phi[i]); + vkd3d_free(block->phi); vkd3d_free(block); }
@@ -6394,7 +6596,6 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t }
sm6->p.shader_desc.ssa_count = sm6->ssa_next_id; - sm6->p.shader_desc.block_count = 1;
if (!(fn = sm6_parser_get_function(sm6, sm6->entry_point))) { diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 2501c325c..9ec1ad0cf 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -3839,7 +3839,12 @@ static uint32_t spirv_compiler_emit_load_ssa_reg(struct spirv_compiler *compiler
ssa = spirv_compiler_get_ssa_register_info(compiler, reg); val_id = ssa->id; - assert(val_id); + if (!val_id) + { + /* Should only be from a missing instruction implementation. */ + assert(compiler->failed); + return 0; + } assert(vkd3d_swizzle_is_scalar(swizzle));
if (reg->dimension == VSIR_DIMENSION_SCALAR) diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index b42090472..d27490432 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -435,6 +435,7 @@ enum vkd3d_shader_opcode VKD3DSIH_NRM, VKD3DSIH_OR, VKD3DSIH_PHASE, + VKD3DSIH_PHI, VKD3DSIH_POW, VKD3DSIH_RCP, VKD3DSIH_REP, @@ -1197,6 +1198,11 @@ static inline bool vsir_register_is_label(const struct vkd3d_shader_register *re return reg->type == VKD3DSPR_LABEL; }
+static inline bool register_is_ssa(const struct vkd3d_shader_register *reg) +{ + return reg->type == VKD3DSPR_SSA; +} + struct vkd3d_shader_param_node { struct vkd3d_shader_param_node *next;
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 13 +++ libs/vkd3d-shader/dxil.c | 140 +++++++++++++++++++++++ libs/vkd3d-shader/ir.c | 2 +- libs/vkd3d-shader/vkd3d_shader_private.h | 2 + 4 files changed, 156 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index fe3133a1a..8e9a394fb 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -830,6 +830,13 @@ static void shader_print_uint_literal(struct vkd3d_d3d_asm_compiler *compiler, prefix, compiler->colours.literal, i, compiler->colours.reset, suffix); }
+static void shader_print_uint64_literal(struct vkd3d_d3d_asm_compiler *compiler, + const char *prefix, uint64_t i, const char *suffix) +{ + vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%"PRIu64"%s%s", + prefix, compiler->colours.literal, i, compiler->colours.reset, suffix); +} + static void shader_print_hex_literal(struct vkd3d_d3d_asm_compiler *compiler, const char *prefix, unsigned int i, const char *suffix) { @@ -1175,6 +1182,12 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const if (reg->dimension == VSIR_DIMENSION_VEC4) shader_print_double_literal(compiler, ", ", reg->u.immconst_f64[1], ""); } + else if (reg->data_type == VKD3D_DATA_UINT64) + { + shader_print_uint64_literal(compiler, "", reg->u.immconst_u64[0], ""); + if (reg->dimension == VSIR_DIMENSION_VEC4) + shader_print_uint64_literal(compiler, "", reg->u.immconst_u64[1], ""); + } else { shader_addline(buffer, "<unhandled data type %#x>", reg->data_type); diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 8d43e6364..e8fe844c4 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -490,15 +490,25 @@ enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, TERMINATOR_COND_BR, + TERMINATOR_SWITCH, TERMINATOR_RET, };
+struct terminator_case +{ + const struct sm6_block *block; + uint64_t value; + bool is_default; +}; + struct sm6_block_terminator { struct vkd3d_shader_register conditional_reg; enum sm6_block_terminator_type type; const struct sm6_block *true_block; const struct sm6_block *false_block; + struct terminator_case *cases; + unsigned int case_count; };
struct sm6_block @@ -1998,6 +2008,13 @@ static inline unsigned int sm6_value_get_constant_uint(const struct sm6_value *v return register_get_uint_value(&value->u.reg); }
+static uint64_t sm6_value_get_constant_uint64(const struct sm6_value *value) +{ + if (!sm6_value_is_constant(value)) + return UINT64_MAX; + return register_get_uint64_value(&value->u.reg); +} + static unsigned int sm6_parser_alloc_ssa_id(struct sm6_parser *sm6) { return sm6->ssa_next_id++; @@ -4537,6 +4554,89 @@ static void sm6_parser_emit_store(struct sm6_parser *sm6, const struct dxil_reco dst_param->reg.alignment = alignment; }
+static void sm6_parser_emit_switch(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) +{ + struct sm6_block_terminator *terminator = &code_block->terminator; + const struct sm6_type *type; + const struct sm6_value *src; + unsigned int i = 1, j; + + if (record->operand_count < 3 || !(record->operand_count & 1)) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for a switch instruction.", record->operand_count); + return; + } + + if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) + return; + + if (!(src = sm6_parser_get_value_by_ref(sm6, record, type, &i)) + || !sm6_value_validate_is_register(src, sm6)) + return; + assert(i == 2); + + if (src->type != type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a switch selector value does not match the selector type."); + } + if (!sm6_type_is_integer(type)) + { + WARN("Selector is not scalar integer.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Selector type class %u of a switch instruction is not scalar integer.", type->class); + return; + } + + terminator->conditional_reg = src->u.reg; + terminator->type = TERMINATOR_SWITCH; + + terminator->case_count = record->operand_count / 2u; + if (!(terminator->cases = vkd3d_calloc(terminator->case_count, sizeof(*terminator->cases)))) + { + ERR("Failed to allocate case array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a switch case array."); + return; + } + + /* Executes 'operand_count / 2' times because operand_count is uneven. */ + for (; i < record->operand_count; i += 2) + { + j = i / 2u - 1; + terminator->cases[j].block = sm6_function_get_block(function, record->operands[i], sm6); + /* For structurisation it is convenient to store the default in the case array. */ + terminator->cases[j].is_default = !j; + } + + for (i = 3; i < record->operand_count; i += 2) + { + if (!(src = sm6_parser_get_value_safe(sm6, record->operands[i]))) + return; + + if (src->type != type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a switch case value does not match the selector type."); + } + if (!sm6_value_is_constant(src)) + { + WARN("Case value is not a constant.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "A switch case value is not a constant."); + } + + terminator->cases[i / 2u].value = sm6_value_get_constant_uint64(src); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + static void sm6_parser_emit_vselect(struct sm6_parser *sm6, const struct dxil_record *record, struct vkd3d_shader_instruction *ins, struct sm6_value *dst) { @@ -5018,6 +5118,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_STORE: sm6_parser_emit_store(sm6, record, ins, dst); break; + case FUNC_CODE_INST_SWITCH: + sm6_parser_emit_switch(sm6, record, function, code_block, ins); + is_terminator = true; + break; case FUNC_CODE_INST_VSELECT: sm6_parser_emit_vselect(sm6, record, ins, dst); break; @@ -5059,6 +5163,7 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ { struct vkd3d_shader_src_param *src_params; struct vkd3d_shader_instruction *ins; + unsigned int i, count;
switch (block->terminator.type) { @@ -5087,6 +5192,40 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ break; }
+ case TERMINATOR_SWITCH: + { + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_SWITCH_MONOLITHIC); + if (!(src_params = instruction_src_params_alloc(ins, block->terminator.case_count * 2u + 1, sm6))) + return; + src_param_init(&src_params[0]); + src_params[0].reg = block->terminator.conditional_reg; + /* TODO: merge block id. */ + vsir_src_param_init_label(&src_params[2], 0); + + for (i = 0, count = 3; i < block->terminator.case_count; ++i) + { + const struct terminator_case *switch_case; + const struct sm6_block *case_block; + + switch_case = &block->terminator.cases[i]; + if (!(case_block = switch_case->block)) + { + assert(sm6->p.failed); + continue; + } + if (switch_case->is_default) + { + vsir_src_param_init_label(&src_params[1], case_block->id); + continue; + } + vsir_src_param_init(&src_params[count], VKD3DSPR_IMMCONST64, VKD3D_DATA_UINT64, 0); + src_params[count++].reg.u.immconst_u64[0] = switch_case->value; + vsir_src_param_init_label(&src_params[count++], case_block->id); + } + + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -6299,6 +6438,7 @@ static void sm6_block_destroy(struct sm6_block *block) for (i = 0; i < block->phi_count; ++i) sm6_phi_destroy(&block->phi[i]); vkd3d_free(block->phi); + vkd3d_free(block->terminator.cases); vkd3d_free(block); }
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 6eb25ef69..5311bb187 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -312,7 +312,7 @@ void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_reg reg->alignment = 0; }
-static void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, +void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, enum vkd3d_data_type data_type, unsigned int idx_count) { vsir_register_init(¶m->reg, reg_type, data_type, idx_count); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index d27490432..bb1ad6f50 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -885,6 +885,8 @@ struct vkd3d_shader_src_param enum vkd3d_shader_src_modifier modifiers; };
+void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, + enum vkd3d_data_type data_type, unsigned int idx_count); void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id);
struct vkd3d_shader_index_range