From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 12 ++ libs/vkd3d-shader/dxil.c | 137 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 15 +++ 3 files changed, 164 insertions(+)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 4934c7b38..994cd396f 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -280,6 +280,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", @@ -1886,6 +1887,17 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, } break;
+ case VKD3DSIH_SWITCH_MONOLITHIC: + { + const struct vkd3d_shader_switch *switch_ = &ins->declaration.switch_; + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_src_param(compiler, ins->src); + for (i = 0; i < switch_->case_count; ++i) + vkd3d_string_buffer_printf(buffer, ", %u: l%u", switch_->cases[i].value, switch_->cases[i].block_id); + vkd3d_string_buffer_printf(buffer, ", default: l%u", switch_->default_id); + break; + } + default: shader_dump_instruction_flags(compiler, ins);
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index b1fc4082e..79142813b 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -466,15 +466,27 @@ enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, TERMINATOR_COND_BR, + TERMINATOR_SWITCH, TERMINATOR_RET, };
+struct terminator_case +{ + struct sm6_block *node; + uint64_t global_order; + uint32_t value; + uint32_t index; + bool is_default; +}; + struct sm6_block_terminator { struct vkd3d_shader_register conditional_reg; enum sm6_block_terminator_type type; struct sm6_block *true_block; struct sm6_block *false_block; + struct terminator_case *cases; + unsigned int case_count; };
struct sm6_block @@ -4269,6 +4281,83 @@ static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record ins->handler_idx = VKD3DSIH_NOP; }
+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; + uint64_t index; + + 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); + + 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) + { + if ((index = record->operands[i]) >= function->block_count) + { + FIXME("Invalid block number %"PRIu64".\n", index); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Switch case block number %"PRIu64" is out of bounds.", index); + return; + } + j = i / 2u - 1; + terminator->cases[j].node = function->blocks[index]; + /* 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_uint(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) { @@ -4508,6 +4597,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const is_terminator = true; ret_found = true; 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; @@ -4570,6 +4663,7 @@ static void sm6_block_emit_terminator(struct sm6_block *block, struct sm6_parser { struct vkd3d_shader_src_param *src_param; struct vkd3d_shader_instruction *ins; + unsigned int i, count;
switch (block->terminator.type) { @@ -4591,6 +4685,49 @@ static void sm6_block_emit_terminator(struct sm6_block *block, struct sm6_parser break; }
+ case TERMINATOR_SWITCH: + { + struct vkd3d_shader_switch_case *cases; + struct terminator_case *switch_case; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_SWITCH_MONOLITHIC); + src_param = instruction_src_params_alloc(ins, 1, sm6); + src_param_init(src_param); + src_param->reg = block->terminator.conditional_reg; + + if (!(cases = vkd3d_calloc(block->terminator.case_count - 1, sizeof(*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; + } + + for (i = 0, count = 0; i < block->terminator.case_count; ++i) + { + switch_case = &block->terminator.cases[i]; + if (switch_case->is_default) + { + ins->declaration.switch_.default_id = switch_case->node->synthetic_id; + continue; + } + cases[count].value = switch_case->value; + cases[count++].block_id = switch_case->node->synthetic_id; + } + assert(ins->declaration.switch_.default_id); + + ins->declaration.switch_.cases = cases; + ins->declaration.switch_.case_count = count; + if (!shader_instruction_array_add_opaque_param(&sm6->p.instructions, cases)) + { + ERR("Failed to store case array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory storing a switch case array."); + vkd3d_free(cases); + } + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index a973c717c..96926058c 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -471,6 +471,7 @@ enum vkd3d_shader_opcode VKD3DSIH_SUB, VKD3DSIH_SWAPC, VKD3DSIH_SWITCH, + VKD3DSIH_SWITCH_MONOLITHIC, VKD3DSIH_SYNC, VKD3DSIH_TEX, VKD3DSIH_TEXBEM, @@ -1111,6 +1112,19 @@ struct vkd3d_shader_branch unsigned int false_id; };
+struct vkd3d_shader_switch_case +{ + unsigned int value; + unsigned int block_id; +}; + +struct vkd3d_shader_switch +{ + const struct vkd3d_shader_switch_case *cases; + unsigned int case_count; + unsigned int default_id; +}; + struct vkd3d_shader_instruction { struct vkd3d_shader_location location; @@ -1151,6 +1165,7 @@ struct vkd3d_shader_instruction struct vkd3d_shader_indexable_temp indexable_temp; struct vkd3d_shader_function_table_pointer fp; struct vkd3d_shader_branch branch; + struct vkd3d_shader_switch switch_; } declaration; };