Multiple fork and join phases are eliminated. Signature elements are merged where required, and all input/output parameters are rewritten.
-- v2: vkd3d-shader/trace: Trace the normalised instruction array after tracing the input. vkd3d-shader/spirv: Normalise signatures and input/output registers to the Shader Model 6 pattern.
From: Conor McCarthy cmccarthy@codeweavers.com
Normalise the incoming vkd3d_shader_instruction IR to the shader model 6 pattern. This allows generation of a single patch constant function in SPIR-V. --- libs/vkd3d-shader/spirv.c | 563 +++++++++++++++-------- libs/vkd3d-shader/vkd3d_shader_private.h | 5 + 2 files changed, 373 insertions(+), 195 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index f0cfee47..0f46c713 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2197,11 +2197,7 @@ struct vkd3d_push_constant_buffer_binding
struct vkd3d_shader_phase { - enum vkd3d_shader_opcode type; - unsigned int idx; - unsigned int instance_count; uint32_t function_id; - uint32_t instance_id; size_t function_location; };
@@ -2276,9 +2272,9 @@ struct spirv_compiler unsigned int output_control_point_count; bool use_vocp;
- unsigned int shader_phase_count; - struct vkd3d_shader_phase *shader_phases; - size_t shader_phases_size; + enum vkd3d_shader_opcode phase; + struct vkd3d_shader_phase control_point_phase; + struct vkd3d_shader_phase patch_constant_phase;
uint32_t current_spec_constant_id; unsigned int spec_constant_count; @@ -2290,9 +2286,19 @@ struct spirv_compiler struct vkd3d_string_buffer_cache string_buffers; };
-static bool is_control_point_phase(const struct vkd3d_shader_phase *phase) +static bool is_in_default_phase(const struct spirv_compiler *compiler) +{ + return compiler->phase == VKD3DSIH_INVALID; +} + +static bool is_in_control_point_phase(const struct spirv_compiler *compiler) +{ + return compiler->phase == VKD3DSIH_HS_CONTROL_POINT_PHASE; +} + +static bool is_in_fork_or_join_phase(const struct spirv_compiler *compiler) { - return phase && phase->type == VKD3DSIH_HS_CONTROL_POINT_PHASE; + return compiler->phase == VKD3DSIH_HS_FORK_PHASE || compiler->phase == VKD3DSIH_HS_JOIN_PHASE; }
static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *compiler); @@ -2437,6 +2443,8 @@ struct spirv_compiler *spirv_compiler_create(const struct vkd3d_shader_version *
compiler->scan_descriptor_info = scan_descriptor_info;
+ compiler->phase = VKD3DSIH_INVALID; + vkd3d_string_buffer_cache_init(&compiler->string_buffers);
spirv_compiler_emit_initial_declarations(compiler); @@ -2887,12 +2895,6 @@ static bool spirv_compiler_get_register_name(char *buffer, unsigned int buffer_s case VKD3DSPR_DEPTHOUTLE: snprintf(buffer, buffer_size, "oDepth"); break; - case VKD3DSPR_FORKINSTID: - snprintf(buffer, buffer_size, "vForkInstanceId"); - break; - case VKD3DSPR_JOININSTID: - snprintf(buffer, buffer_size, "vJoinInstanceId"); - break; case VKD3DSPR_GSINSTID: snprintf(buffer, buffer_size, "vGSInstanceID"); break; @@ -4302,7 +4304,7 @@ static uint32_t spirv_compiler_emit_load_invocation_id(struct spirv_compiler *co }
static void spirv_compiler_emit_shader_phase_name(struct spirv_compiler *compiler, - uint32_t id, const struct vkd3d_shader_phase *phase, const char *suffix) + uint32_t id, const char *suffix) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const char *name; @@ -4310,7 +4312,7 @@ static void spirv_compiler_emit_shader_phase_name(struct spirv_compiler *compile if (!suffix) suffix = "";
- switch (phase->type) + switch (compiler->phase) { case VKD3DSIH_HS_CONTROL_POINT_PHASE: name = "control"; @@ -4322,58 +4324,19 @@ static void spirv_compiler_emit_shader_phase_name(struct spirv_compiler *compile name = "join"; break; default: - ERR("Invalid phase type %#x.\n", phase->type); + ERR("Invalid phase type %#x.\n", compiler->phase); return; } - vkd3d_spirv_build_op_name(builder, id, "%s%u%s", name, phase->idx, suffix); -} - -static void spirv_compiler_begin_shader_phase(struct spirv_compiler *compiler, - struct vkd3d_shader_phase *phase) -{ - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - uint32_t void_id, function_type_id; - unsigned int param_count; - uint32_t param_type_id; - - if (phase->instance_count) - { - param_type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_UINT, 1); - param_count = 1; - } - else - { - param_count = 0; - } - - phase->function_id = vkd3d_spirv_alloc_id(builder); - - void_id = vkd3d_spirv_get_op_type_void(builder); - function_type_id = vkd3d_spirv_get_op_type_function(builder, void_id, ¶m_type_id, param_count); - vkd3d_spirv_build_op_function(builder, void_id, phase->function_id, - SpvFunctionControlMaskNone, function_type_id); - - if (phase->instance_count) - phase->instance_id = vkd3d_spirv_build_op_function_parameter(builder, param_type_id); - - vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder)); - phase->function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); - - spirv_compiler_emit_shader_phase_name(compiler, phase->function_id, phase, NULL); + vkd3d_spirv_build_op_name(builder, id, "%s%s", name, suffix); }
static const struct vkd3d_shader_phase *spirv_compiler_get_current_shader_phase( struct spirv_compiler *compiler) { - struct vkd3d_shader_phase *phase; - - if (!compiler->shader_phase_count) + if (is_in_default_phase(compiler)) return NULL;
- phase = &compiler->shader_phases[compiler->shader_phase_count - 1]; - if (!phase->function_id) - spirv_compiler_begin_shader_phase(compiler, phase); - return phase; + return is_in_control_point_phase(compiler) ? &compiler->control_point_phase : &compiler->patch_constant_phase; }
static void spirv_compiler_decorate_xfb_output(struct spirv_compiler *compiler, @@ -4770,12 +4733,9 @@ static void spirv_compiler_emit_input_register(struct spirv_compiler *compiler, }
static void spirv_compiler_emit_shader_phase_input(struct spirv_compiler *compiler, - const struct vkd3d_shader_phase *phase, const struct vkd3d_shader_dst_param *dst) + const struct vkd3d_shader_dst_param *dst) { - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_shader_register *reg = &dst->reg; - struct vkd3d_symbol reg_symbol; - uint32_t val_id;
switch (reg->type) { @@ -4787,10 +4747,6 @@ static void spirv_compiler_emit_shader_phase_input(struct spirv_compiler *compil case VKD3DSPR_PRIMID: spirv_compiler_emit_input_register(compiler, dst); return; - case VKD3DSPR_FORKINSTID: - case VKD3DSPR_JOININSTID: - val_id = phase->instance_id; - break; case VKD3DSPR_OUTPOINTID: /* Emitted in spirv_compiler_emit_initial_declarations(). */ case VKD3DSPR_OUTCONTROLPOINT: /* See spirv_compiler_leave_shader_phase(). */ return; @@ -4798,13 +4754,6 @@ static void spirv_compiler_emit_shader_phase_input(struct spirv_compiler *compil FIXME("Unhandled shader phase input register %#x.\n", reg->type); return; } - - vkd3d_symbol_make_register(®_symbol, reg); - vkd3d_symbol_set_register_info(®_symbol, val_id, - SpvStorageClassMax /* Intermediate value */, - VKD3D_SHADER_COMPONENT_UINT, VKD3DSP_WRITEMASK_0); - spirv_compiler_put_symbol(compiler, ®_symbol); - spirv_compiler_emit_register_debug_name(builder, val_id, reg); }
static unsigned int spirv_compiler_get_output_variable_index( @@ -4977,7 +4926,7 @@ static void spirv_compiler_emit_output_register(struct spirv_compiler *compiler, }
static uint32_t spirv_compiler_emit_shader_phase_builtin_variable(struct spirv_compiler *compiler, - const struct vkd3d_shader_phase *phase, const struct vkd3d_spirv_builtin *builtin) + const struct vkd3d_spirv_builtin *builtin) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; uint32_t *variable_id, id; @@ -4993,7 +4942,7 @@ static uint32_t spirv_compiler_emit_shader_phase_builtin_variable(struct spirv_c return *variable_id;
id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0); - if (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE) + if (is_in_fork_or_join_phase(compiler)) vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0);
if (variable_id) @@ -5011,7 +4960,6 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, unsigned int component_idx, output_component_count; enum vkd3d_shader_component_type component_type; const struct vkd3d_spirv_builtin *builtin; - const struct vkd3d_shader_phase *phase; struct vkd3d_symbol *symbol = NULL; bool use_private_variable = false; struct vkd3d_symbol reg_symbol; @@ -5023,12 +4971,11 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, bool is_patch_constant; uint32_t id, var_id;
- phase = spirv_compiler_get_current_shader_phase(compiler); - is_patch_constant = phase && (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE); + is_patch_constant = is_in_fork_or_join_phase(compiler);
shader_signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature;
- array_size = is_control_point_phase(phase) ? compiler->output_control_point_count : 0; + array_size = is_in_control_point_phase(compiler) ? compiler->output_control_point_count : 0;
if (!(signature_element = vkd3d_find_signature_element_for_reg(shader_signature, &signature_idx, reg->idx[0].offset, dst->write_mask))) @@ -5083,8 +5030,8 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, } else if (builtin) { - if (phase) - id = spirv_compiler_emit_shader_phase_builtin_variable(compiler, phase, builtin); + if (spirv_compiler_get_current_shader_phase(compiler)) + id = spirv_compiler_emit_shader_phase_builtin_variable(compiler, builtin); else id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size);
@@ -5143,7 +5090,7 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, use_private_variable ? VKD3D_SHADER_COMPONENT_FLOAT : component_type, use_private_variable ? VKD3DSP_WRITEMASK_ALL : write_mask); reg_symbol.info.reg.is_aggregate = use_private_variable ? is_patch_constant : array_size; - if (!use_private_variable && is_control_point_phase(phase)) + if (!use_private_variable && is_in_control_point_phase(compiler)) { reg_symbol.info.reg.member_idx = spirv_compiler_get_invocation_id(compiler); reg_symbol.info.reg.is_dynamically_indexed = true; @@ -5295,7 +5242,6 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler * uint32_t void_id, type_id, ptr_type_id, function_type_id, function_id; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_shader_signature *signature; - const struct vkd3d_shader_phase *phase; uint32_t output_index_id = 0; bool is_patch_constant; unsigned int i, count; @@ -5306,8 +5252,7 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler * STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(compiler->private_output_variable_array_idx)); STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(compiler->private_output_variable_write_mask));
- phase = spirv_compiler_get_current_shader_phase(compiler); - is_patch_constant = phase && (phase->type == VKD3DSIH_HS_FORK_PHASE || phase->type == VKD3DSIH_HS_JOIN_PHASE); + is_patch_constant = is_in_fork_or_join_phase(compiler);
signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature;
@@ -5340,7 +5285,7 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler * param_id[i] = vkd3d_spirv_build_op_load(builder, type_id, param_id[i], SpvMemoryAccessMaskNone); }
- if (is_control_point_phase(phase)) + if (is_in_control_point_phase(compiler)) output_index_id = spirv_compiler_emit_load_invocation_id(compiler);
for (i = 0; i < signature->element_count; ++i) @@ -6185,10 +6130,9 @@ static void spirv_compiler_emit_dcl_input(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { const struct vkd3d_shader_dst_param *dst = &instruction->declaration.dst; - const struct vkd3d_shader_phase *phase;
- if ((phase = spirv_compiler_get_current_shader_phase(compiler))) - spirv_compiler_emit_shader_phase_input(compiler, phase, dst); + if (spirv_compiler_get_current_shader_phase(compiler)) + spirv_compiler_emit_shader_phase_input(compiler, dst); else if (vkd3d_shader_register_is_input(&dst->reg) || dst->reg.type == VKD3DSPR_PATCHCONST) spirv_compiler_emit_input(compiler, dst, VKD3D_SIV_NONE, VKD3DSIM_NONE); else @@ -6495,8 +6439,7 @@ static void spirv_compiler_emit_dcl_thread_group(struct spirv_compiler *compiler SpvExecutionModeLocalSize, local_size, ARRAY_SIZE(local_size)); }
-static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler, - const struct vkd3d_shader_phase *phase) +static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler) { const struct vkd3d_shader_signature *signature = compiler->output_signature; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; @@ -6515,11 +6458,11 @@ static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler, * point phase. Reinsert symbols for vocp registers while leaving the * control point phase. */ - if (is_control_point_phase(phase)) + if (is_in_control_point_phase(compiler)) { if (compiler->epilogue_function_id) { - spirv_compiler_emit_shader_phase_name(compiler, compiler->epilogue_function_id, phase, "_epilogue"); + spirv_compiler_emit_shader_phase_name(compiler, compiler->epilogue_function_id, "_epilogue"); spirv_compiler_emit_shader_epilogue_function(compiler); }
@@ -6560,79 +6503,38 @@ static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler, } } } - - if (phase->instance_count) - { - memset(®, 0, sizeof(reg)); - reg.type = phase->type == VKD3DSIH_HS_FORK_PHASE ? VKD3DSPR_FORKINSTID : VKD3DSPR_JOININSTID; - reg.idx[0].offset = ~0u; - reg.idx[1].offset = ~0u; - vkd3d_symbol_make_register(®_symbol, ®); - if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) - { - rb_remove(&compiler->symbol_table, entry); - vkd3d_symbol_free(entry, NULL); - } - } }
static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - const struct vkd3d_shader_phase *previous_phase; + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + uint32_t function_id, void_id, function_type_id; struct vkd3d_shader_phase *phase;
- if ((previous_phase = spirv_compiler_get_current_shader_phase(compiler))) - spirv_compiler_leave_shader_phase(compiler, previous_phase); - - if (!vkd3d_array_reserve((void **)&compiler->shader_phases, &compiler->shader_phases_size, - compiler->shader_phase_count + 1, sizeof(*compiler->shader_phases))) + if (is_in_fork_or_join_phase(compiler) && (instruction->handler_idx == VKD3DSIH_HS_JOIN_PHASE + || instruction->handler_idx == VKD3DSIH_HS_FORK_PHASE)) return; - phase = &compiler->shader_phases[compiler->shader_phase_count];
- phase->type = instruction->handler_idx; - phase->idx = compiler->shader_phase_count; - phase->instance_count = 0; - phase->function_id = 0; - phase->instance_id = 0; - phase->function_location = 0; + if (compiler->phase != VKD3DSIH_INVALID) + spirv_compiler_leave_shader_phase(compiler);
- ++compiler->shader_phase_count; -} + function_id = vkd3d_spirv_alloc_id(builder);
-static int spirv_compiler_emit_shader_phase_instance_count(struct spirv_compiler *compiler, - const struct vkd3d_shader_instruction *instruction) -{ - struct vkd3d_shader_phase *phase = &compiler->shader_phases[compiler->shader_phase_count - 1]; - - if (!compiler->shader_phase_count - || (phase->type != VKD3DSIH_HS_FORK_PHASE && phase->type != VKD3DSIH_HS_JOIN_PHASE) - || phase->function_id) - { - WARN("Unexpected dcl_hs_{fork,join}_phase_instance_count instruction.\n"); - return VKD3D_ERROR_INVALID_SHADER; - } - - phase->instance_count = instruction->declaration.count; - - spirv_compiler_begin_shader_phase(compiler, phase); - - return VKD3D_OK; -} - -static const struct vkd3d_shader_phase *spirv_compiler_get_control_point_phase( - struct spirv_compiler *compiler) -{ - const struct vkd3d_shader_phase *phase; + void_id = vkd3d_spirv_get_op_type_void(builder); + function_type_id = vkd3d_spirv_get_op_type_function(builder, void_id, NULL, 0); + vkd3d_spirv_build_op_function(builder, void_id, function_id, + SpvFunctionControlMaskNone, function_type_id);
- if (compiler->shader_phase_count < 1) - return NULL; + vkd3d_spirv_build_op_label(builder, vkd3d_spirv_alloc_id(builder));
- phase = &compiler->shader_phases[0]; - if (is_control_point_phase(phase)) - return phase; + compiler->phase = instruction->handler_idx; + spirv_compiler_emit_shader_phase_name(compiler, function_id, NULL);
- return 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); }
static void spirv_compiler_emit_default_control_point_phase(struct spirv_compiler *compiler) @@ -6854,9 +6756,6 @@ static void spirv_compiler_emit_shader_epilogue_invocation(struct spirv_compiler static void spirv_compiler_emit_hull_shader_main(struct spirv_compiler *compiler) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - const struct vkd3d_shader_phase *control_point_phase, *phase; - uint32_t phase_instance_id; - unsigned int i, j; uint32_t void_id;
vkd3d_spirv_builder_begin_main_function(builder); @@ -6865,35 +6764,16 @@ static void spirv_compiler_emit_hull_shader_main(struct spirv_compiler *compiler
void_id = vkd3d_spirv_get_op_type_void(builder);
- if ((control_point_phase = spirv_compiler_get_control_point_phase(compiler))) - vkd3d_spirv_build_op_function_call(builder, void_id, control_point_phase->function_id, NULL, 0); + if (compiler->control_point_phase.function_id) + vkd3d_spirv_build_op_function_call(builder, void_id, compiler->control_point_phase.function_id, NULL, 0); else spirv_compiler_emit_default_control_point_phase(compiler);
- if (compiler->use_vocp) - spirv_compiler_emit_hull_shader_barrier(compiler); - - for (i = 0; i < compiler->shader_phase_count; ++i) - { - phase = &compiler->shader_phases[i]; - if (is_control_point_phase(phase)) - continue; - - if (phase->instance_count) - { - for (j = 0; j < phase->instance_count; ++j) - { - phase_instance_id = spirv_compiler_get_constant_uint(compiler, j); - vkd3d_spirv_build_op_function_call(builder, - void_id, phase->function_id, &phase_instance_id, 1); - } - } - else - { - vkd3d_spirv_build_op_function_call(builder, void_id, phase->function_id, NULL, 0); - } - } - + /* TODO: only call the patch constant function for invocation 0. The simplest way + * is to avoid use of private variables there, otherwise we would need a separate + * patch constant epilogue also only called from invocation 0. */ + spirv_compiler_emit_hull_shader_barrier(compiler); + vkd3d_spirv_build_op_function_call(builder, void_id, compiler->patch_constant_phase.function_id, NULL, 0); spirv_compiler_emit_shader_epilogue_invocation(compiler); vkd3d_spirv_build_op_return(builder); vkd3d_spirv_build_op_function_end(builder); @@ -7575,10 +7455,10 @@ static uint32_t spirv_compiler_emit_conditional_branch(struct spirv_compiler *co static void spirv_compiler_emit_return(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - const struct vkd3d_shader_phase *phase = spirv_compiler_get_current_shader_phase(compiler); struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
- if (compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY && (!phase || is_control_point_phase(phase))) + if (compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY && (is_in_default_phase(compiler) + || is_in_control_point_phase(compiler))) spirv_compiler_emit_shader_epilogue_invocation(compiler);
vkd3d_spirv_build_op_return(builder); @@ -9699,10 +9579,6 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_DCL_THREAD_GROUP: spirv_compiler_emit_dcl_thread_group(compiler, instruction); break; - case VKD3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT: - case VKD3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT: - ret = spirv_compiler_emit_shader_phase_instance_count(compiler, instruction); - break; case VKD3DSIH_HS_CONTROL_POINT_PHASE: case VKD3DSIH_HS_FORK_PHASE: case VKD3DSIH_HS_JOIN_PHASE: @@ -9947,6 +9823,294 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, return ret; }
+struct vkd3d_shader_normaliser +{ + struct vkd3d_shader_instruction_array instructions; + + unsigned int max_temp_count; + unsigned int temp_dcl_idx; + uint64_t temp_is_instance_id; + + unsigned int instance_count; + unsigned int phase_body_idx; + enum vkd3d_shader_opcode phase; +}; + +static inline bool shader_normaliser_new_instructions(struct vkd3d_shader_normaliser *normaliser, unsigned int extra) +{ + return shader_instruction_array_reserve(&normaliser->instructions, normaliser->instructions.count + extra); +} + +static void shader_normaliser_destroy(struct vkd3d_shader_normaliser *normaliser) +{ + shader_instruction_array_destroy(&normaliser->instructions); +} + +static inline bool shader_src_param_is_phase_instance_id(const struct vkd3d_shader_src_param *param, + const struct vkd3d_shader_normaliser *normaliser) +{ + const struct vkd3d_shader_register *reg = ¶m->reg; + return vkd3d_shader_register_is_phase_instance_id(reg) || (reg->type == VKD3DSPR_TEMP + && !vkd3d_swizzle_get_component(param->swizzle, 0) + && (normaliser->temp_is_instance_id & (1ull << reg->idx[0].offset))); +} + +static void shader_register_eliminate_phase_addressing(struct vkd3d_shader_register *reg, + unsigned int instance_id, const struct vkd3d_shader_normaliser *normaliser) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reg->idx) && reg->idx[i].offset != ~0u; ++i) + { + if (reg->idx[i].rel_addr && shader_src_param_is_phase_instance_id(reg->idx[i].rel_addr, normaliser)) + { + reg->idx[i].rel_addr = NULL; + reg->idx[i].offset += instance_id; + } + } +} + +static void shader_instruction_eliminate_phase_instance_id(struct vkd3d_shader_instruction *ins, + unsigned int instance_id, const struct vkd3d_shader_normaliser *normaliser) +{ + struct vkd3d_shader_register *reg; + unsigned int i; + + for (i = 0; i < ins->src_count; ++i) + { + reg = (struct vkd3d_shader_register *)&ins->src[i].reg; + if (shader_src_param_is_phase_instance_id(&ins->src[i], normaliser)) + { + reg->type = VKD3DSPR_IMMCONST; + reg->precision = VKD3D_SHADER_REGISTER_PRECISION_DEFAULT; + reg->non_uniform = false; + reg->data_type = VKD3D_DATA_UINT; + reg->idx[0].offset = ~0u; + reg->idx[0].rel_addr = NULL; + reg->idx[1].offset = ~0u; + reg->idx[1].rel_addr = NULL; + reg->idx[2].offset = ~0u; + reg->idx[2].rel_addr = NULL; + reg->immconst_type = VKD3D_IMMCONST_SCALAR; + reg->u.immconst_uint[0] = instance_id; + continue; + } + shader_register_eliminate_phase_addressing(reg, instance_id, normaliser); + } + + for (i = 0; i < ins->dst_count; ++i) + shader_register_eliminate_phase_addressing((struct vkd3d_shader_register *)&ins->dst[i].reg, + instance_id, normaliser); +} + +static bool shader_register_clone_relative_addresses(struct vkd3d_shader_register *reg, + struct vkd3d_shader_instruction_array *instructions) +{ + struct vkd3d_shader_src_param *src; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reg->idx) && reg->idx[i].offset != ~0u; ++i) + { + if (!reg->idx[i].rel_addr) + continue; + + if (!(src = shader_src_param_allocator_get(&instructions->src_params, 1))) + return false; + + memcpy(src, reg->idx[i].rel_addr, sizeof(*src)); + reg->idx[i].rel_addr = src; + } + + return true; +} + +static bool shader_instruction_array_clone_instruction(struct vkd3d_shader_instruction_array *instructions, + unsigned int dst, const struct vkd3d_shader_instruction *src) +{ + struct vkd3d_shader_instruction *ins = &instructions->elements[dst]; + struct vkd3d_shader_dst_param *dst_params; + struct vkd3d_shader_src_param *src_params; + unsigned int i; + + *ins = *src; + + if (ins->dst_count && ins->dst) + { + if (!(dst_params = shader_dst_param_allocator_get(&instructions->dst_params, ins->dst_count))) + return false; + + memcpy(dst_params, ins->dst, ins->dst_count * sizeof(*ins->dst)); + ins->dst = dst_params; + for (i = 0; i < ins->dst_count; ++i) + { + if (!shader_register_clone_relative_addresses(&dst_params[i].reg, instructions)) + return false; + } + } + if (ins->src_count) + { + if (!(src_params = shader_src_param_allocator_get(&instructions->src_params, ins->src_count))) + return false; + + memcpy(src_params, ins->src, ins->src_count * sizeof(*ins->src)); + ins->src = src_params; + for (i = 0; i < ins->src_count; ++i) + { + if (!shader_register_clone_relative_addresses(&src_params[i].reg, instructions)) + return false; + } + } + + return true; +} + +static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vkd3d_shader_normaliser *normaliser, + unsigned int *instruction_count) +{ + struct vkd3d_shader_instruction *ins = &normaliser->instructions.elements[normaliser->instructions.count]; + unsigned int i, j, count; + + if (ins->handler_idx == VKD3DSIH_HS_FORK_PHASE || ins->handler_idx == VKD3DSIH_HS_JOIN_PHASE) + { + /* Leave the first occurrence and delete the rest. */ + *instruction_count = normaliser->phase != ins->handler_idx; + /* Reset the phase info. */ + normaliser->phase_body_idx = ~0u; + normaliser->phase = ins->handler_idx; + normaliser->instance_count = 1; + normaliser->temp_is_instance_id = 0; + return VKD3D_OK; + } + else if (ins->handler_idx == VKD3DSIH_DCL_HS_FORK_PHASE_INSTANCE_COUNT + || ins->handler_idx == VKD3DSIH_DCL_HS_JOIN_PHASE_INSTANCE_COUNT) + { + normaliser->instance_count = ins->declaration.count + !ins->declaration.count; + *instruction_count = 0; + return VKD3D_OK; + } + else if (ins->handler_idx == VKD3DSIH_DCL_INPUT && vkd3d_shader_register_is_phase_instance_id( + &ins->declaration.dst.reg)) + { + *instruction_count = 0; + return VKD3D_OK; + } + else if (ins->handler_idx == VKD3DSIH_DCL_TEMPS && normaliser->phase != VKD3DSIH_INVALID) + { + /* Leave only the first temp declaration and set it to the max count later. */ + if (!normaliser->max_temp_count) + normaliser->temp_dcl_idx = normaliser->instructions.count; + else + *instruction_count = 0; + normaliser->max_temp_count = max(normaliser->max_temp_count, ins->declaration.count); + return VKD3D_OK; + } + + if (normaliser->phase == VKD3DSIH_INVALID || is_dcl_instruction(ins->handler_idx)) + return VKD3D_OK; + + if (ins->handler_idx == VKD3DSIH_MOV && vkd3d_shader_register_is_phase_instance_id(&ins->src->reg)) + { + if (ins->dst->reg.type != VKD3DSPR_TEMP) + { + FIXME("Instance id not assigned to temp register.\n"); + return VKD3D_OK; + } + /* Instance id always seems to be assigned to r{n}.x */ + if (ins->dst->write_mask != VKD3DSP_WRITEMASK_0) + FIXME("Unsupported write mask %#x.\n", ins->dst->write_mask); + if (ins->dst->reg.idx[0].offset >= sizeof(normaliser->temp_is_instance_id) * CHAR_BIT) + FIXME("Unsupported temp idx %u.\n", ins->dst->reg.idx[0].offset); + else + normaliser->temp_is_instance_id |= 1ull << ins->dst->reg.idx[0].offset; + *instruction_count = 0; + return VKD3D_OK; + } + + if (normaliser->phase_body_idx == ~0u) + normaliser->phase_body_idx = normaliser->instructions.count; + + if (ins->handler_idx != VKD3DSIH_RET) + return VKD3D_OK; + + count = normaliser->instructions.count - normaliser->phase_body_idx; + + if (!shader_instruction_array_reserve(&normaliser->instructions, count * (normaliser->instance_count - 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + /* Make a copy of the non-dcl instructions for each instance. */ + ins = &normaliser->instructions.elements[normaliser->phase_body_idx]; + for (i = 1; i < normaliser->instance_count; ++i) + { + for (j = 0; j < count; ++j) + { + if (!shader_instruction_array_clone_instruction(&normaliser->instructions, + normaliser->phase_body_idx + count * i + j, &ins[j])) + return VKD3D_ERROR_OUT_OF_MEMORY; + } + } + /* Replace each reference to the instance id with a constant instance id. */ + for (i = 0; i < normaliser->instance_count; ++i) + { + for (j = 0; j < count; ++j) + shader_instruction_eliminate_phase_instance_id( + &normaliser->instructions.elements[normaliser->phase_body_idx + count * i + j], i, normaliser); + } + + *instruction_count = count * (normaliser->instance_count - 1); + return VKD3D_OK; +} + +static void shader_instruction_init(struct vkd3d_shader_instruction *ins, enum vkd3d_shader_opcode handler_idx) +{ + memset(ins, 0, sizeof(*ins)); + ins->handler_idx = handler_idx; +} + +static enum vkd3d_result shader_normaliser_normalise(struct vkd3d_shader_normaliser *normaliser, + const struct vkd3d_shader_instruction_array *instructions, struct spirv_compiler *compiler) +{ + const struct vkd3d_shader_instruction *ins; + enum vkd3d_result result = VKD3D_OK; + unsigned int i, instruction_count; + + memset(normaliser, 0, sizeof(*normaliser)); + normaliser->phase = VKD3DSIH_INVALID; + if (!shader_instruction_array_init(&normaliser->instructions, instructions->count)) + { + ERR("Failed to allocate instructions.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + for (i = 0; i < instructions->count; ++i) + { + if (!shader_normaliser_new_instructions(normaliser, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &instructions->elements[i]; + + if (!shader_instruction_array_clone_instruction(&normaliser->instructions, normaliser->instructions.count, ins)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + instruction_count = 1; + if ((result = shader_normaliser_eliminate_phase_instance_id(normaliser, &instruction_count)) < 0) + return result; + + normaliser->instructions.count += instruction_count; + } + + if (normaliser->phase != VKD3DSIH_INVALID) + { + if (normaliser->temp_dcl_idx) + normaliser->instructions.elements[normaliser->temp_dcl_idx].declaration.count = normaliser->max_temp_count; + + if (!shader_normaliser_new_instructions(normaliser, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + shader_instruction_init(&normaliser->instructions.elements[normaliser->instructions.count++], VKD3DSIH_RET); + } + + return result; +} + int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_parser *parser, struct vkd3d_shader_code *spirv) @@ -9955,18 +10119,28 @@ int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, const struct vkd3d_shader_spirv_target_info *info = compiler->spirv_target_info; const struct vkd3d_shader_spirv_domain_shader_target_info *ds_info; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - const struct vkd3d_shader_phase *phase; + struct vkd3d_shader_normaliser normaliser; enum vkd3d_result result = VKD3D_OK; unsigned int i;
- for (i = 0; i < instructions->count; ++i) + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) { - if ((result = spirv_compiler_handle_instruction(compiler, &instructions->elements[i])) < 0) + if ((result = shader_normaliser_normalise(&normaliser, instructions, compiler)) < 0) return result; + instructions = &normaliser.instructions; }
- if ((phase = spirv_compiler_get_current_shader_phase(compiler))) - spirv_compiler_leave_shader_phase(compiler, phase); + for (i = 0; i < instructions->count && result >= 0; ++i) + result = spirv_compiler_handle_instruction(compiler, &instructions->elements[i]); + + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) + shader_normaliser_destroy(&normaliser); + + if (result < 0) + return result; + + if (!is_in_default_phase(compiler)) + spirv_compiler_leave_shader_phase(compiler); else vkd3d_spirv_build_op_function_end(builder);
@@ -10034,7 +10208,6 @@ void spirv_compiler_destroy(struct spirv_compiler *compiler)
rb_destroy(&compiler->symbol_table, vkd3d_symbol_free, NULL);
- vkd3d_free(compiler->shader_phases); vkd3d_free(compiler->spec_constants);
vkd3d_string_buffer_cache_cleanup(&compiler->string_buffers); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 6c5a1917..2108f6e8 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -924,6 +924,11 @@ static inline bool vkd3d_shader_register_is_output(const struct vkd3d_shader_reg return reg->type == VKD3DSPR_OUTPUT || reg->type == VKD3DSPR_COLOROUT; }
+static inline bool vkd3d_shader_register_is_phase_instance_id(const struct vkd3d_shader_register *reg) +{ + return reg->type == VKD3DSPR_FORKINSTID || reg->type == VKD3DSPR_JOININSTID; +} + struct vkd3d_shader_location { const char *source_name;
From: Conor McCarthy cmccarthy@codeweavers.com
--- tests/d3d12.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+)
diff --git a/tests/d3d12.c b/tests/d3d12.c index ea569bc3..fdd349dc 100644 --- a/tests/d3d12.c +++ b/tests/d3d12.c @@ -36103,6 +36103,165 @@ static void test_clock_calibration(void) destroy_test_context(&context); }
+static void test_vs_ps_relative_addressing(void) +{ + D3D12_ROOT_SIGNATURE_DESC root_signature_desc; + D3D12_ROOT_PARAMETER root_parameters[1]; + ID3D12GraphicsCommandList *command_list; + D3D12_INPUT_LAYOUT_DESC input_layout; + struct test_context_desc desc; + D3D12_VERTEX_BUFFER_VIEW vbv; + struct test_context context; + ID3D12CommandQueue *queue; + ID3D12Resource *vb; + HRESULT hr; + + static const struct + { + struct vec4 position; + uint32_t color[3]; + } + vertices[] = + { + {{-1.0f, -1.0f, 0.0f, 1.0f}, {0xffffff00, 0xff00, 0xff00ff00}}, + {{-1.0f, 1.0f, 0.0f, 1.0f}, {0xffffff00, 0xff00, 0xff00ff00}}, + {{ 1.0f, -1.0f, 0.0f, 1.0f}, {0xffffff00, 0xff00, 0xff00ff00}}, + {{ 1.0f, 1.0f, 0.0f, 1.0f}, {0xffffff00, 0xff00, 0xff00ff00}}, + }; + static const D3D12_INPUT_ELEMENT_DESC layout_desc[] = + { + {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + {"COLOR", 2, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, + }; + static const DWORD vs_code[] = + { +#if 0 + uint3 index; + + struct vs_data + { + float4 pos : SV_Position; + float4 color[3] : COLOR; + }; + + void main(in struct vs_data vs_input, out struct vs_data vs_output) + { + vs_output.pos = vs_input.pos; + vs_output.color[0] = vs_input.color[index.x]; + vs_output.color[1] = vs_input.color[index.y]; + vs_output.color[2] = vs_input.color[index.z]; + } +#endif + 0x43425844, 0x313cf242, 0x30e9b93c, 0xf8d3ed69, 0x0feecdca, 0x00000001, 0x00000288, 0x00000003, + 0x0000002c, 0x000000b0, 0x00000134, 0x4e475349, 0x0000007c, 0x00000004, 0x00000008, 0x00000068, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000f0f, 0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002, + 0x00000f0f, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000f0f, 0x505f5653, + 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x4e47534f, 0x0000007c, 0x00000004, 0x00000008, + 0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0x00000074, 0x00000001, 0x00000000, 0x00000003, + 0x00000002, 0x0000000f, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x0000000f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x58454853, 0x0000014c, 0x00010050, + 0x00000053, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005f, 0x001010f2, + 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x0300005f, 0x001010f2, 0x00000002, 0x0300005f, + 0x001010f2, 0x00000003, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, + 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000003, 0x02000068, + 0x00000001, 0x0400005b, 0x001010f2, 0x00000001, 0x00000003, 0x05000036, 0x001020f2, 0x00000000, + 0x00101e46, 0x00000000, 0x06000036, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, + 0x07000036, 0x001020f2, 0x00000001, 0x00d01e46, 0x00000001, 0x0010000a, 0x00000000, 0x06000036, + 0x00100012, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x07000036, 0x001020f2, 0x00000002, + 0x00d01e46, 0x00000001, 0x0010000a, 0x00000000, 0x06000036, 0x00100012, 0x00000000, 0x0020802a, + 0x00000000, 0x00000000, 0x07000036, 0x001020f2, 0x00000003, 0x00d01e46, 0x00000001, 0x0010000a, + 0x00000000, 0x0100003e, + }; + static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)}; + static const DWORD ps_code[] = + { +#if 0 + uint4 index; + + struct ps_data + { + float4 pos : SV_Position; + float4 color[3] : COLOR; + }; + + float4 main(struct ps_data ps_input) : SV_Target + { + return ps_input.color[index.w]; + } +#endif + 0x43425844, 0x2b11c807, 0xf4f69d91, 0x983d18c9, 0x99ff2a5e, 0x00000001, 0x00000188, 0x00000003, + 0x0000002c, 0x000000b0, 0x000000e4, 0x4e475349, 0x0000007c, 0x00000004, 0x00000008, 0x00000068, + 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000f0f, 0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002, + 0x00000f0f, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000f0f, 0x505f5653, + 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, + 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, + 0xabab0074, 0x58454853, 0x0000009c, 0x00000050, 0x00000027, 0x0100086a, 0x04000059, 0x00208e46, + 0x00000000, 0x00000001, 0x03001062, 0x001010f2, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03001062, 0x001010f2, 0x00000003, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, + 0x0400005b, 0x001010f2, 0x00000001, 0x00000003, 0x06000036, 0x00100012, 0x00000000, 0x0020803a, + 0x00000000, 0x00000000, 0x07000036, 0x001020f2, 0x00000000, 0x00d01e46, 0x00000001, 0x0010000a, + 0x00000000, 0x0100003e, + }; + static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)}; + static const uint32_t indices[] = {1, 2, 0, 1}; + static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f}; + + memset(&desc, 0, sizeof(desc)); + desc.no_root_signature = true; + desc.no_pipeline = true; + if (!init_test_context(&context, &desc)) + return; + command_list = context.list; + queue = context.queue; + + root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + root_parameters[0].Constants.ShaderRegister = 0; + root_parameters[0].Constants.RegisterSpace = 0; + root_parameters[0].Constants.Num32BitValues = 4; + root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + memset(&root_signature_desc, 0, sizeof(root_signature_desc)); + root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters); + root_signature_desc.pParameters = root_parameters; + root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + input_layout.pInputElementDescs = layout_desc; + input_layout.NumElements = ARRAY_SIZE(layout_desc); + context.pipeline_state = create_pipeline_state(context.device, + context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout); + + vb = create_upload_buffer(context.device, sizeof(vertices), vertices); + vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb); + vbv.StrideInBytes = sizeof(*vertices); + vbv.SizeInBytes = sizeof(vertices); + + ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL); + + ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL); + ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature); + ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state); + ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &indices, 0); + ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv); + ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport); + ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect); + ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0); + + transition_resource_state(command_list, context.render_target, + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE); + todo + check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0); + + ID3D12Resource_Release(vb); + destroy_test_context(&context); +} + START_TEST(d3d12) { parse_args(argc, argv); @@ -36280,4 +36439,5 @@ START_TEST(d3d12) run_test(test_unbounded_resource_arrays); run_test(test_unbounded_samplers); run_test(test_clock_calibration); + run_test(test_vs_ps_relative_addressing); }
From: Conor McCarthy cmccarthy@codeweavers.com
In Shader Model 6 each signature element can span a range of register indices, or 'rows', and system values do not share a register index with non-system values. Inputs and outputs are referenced by element index instead of register index. This patch merges multiple signature elements into a single element under the following conditions:
- The register index in a load or store is specified dynamically by including a relative address parameter with a base register index. The dcl_index_range instruction is used to identify these. - A register declaration is split across multiple elements which declare different components of the register. - Storage of tessellation factors in patch constant functions. These are an array in SPIR-V, but in SM 5 each factor is declared as a separate register, and these are dynamically indexed by the fork/join instance id. Elimination of multiple fork/join phases converts the indices to constants, but merging the signature elements into a single arrayed element matches the SPIR-V output.
All references to input/output register indices are converted to element indices. If a relative address is present, the element index is moved up a slot so it cannot be confused with a constant offset. Relative addressing of register indices was only handled for tessellation factors. This patch adds generic suport for it.
spirv_compiler_emit_hull_shader_inputs() is incompatible with this patch, so input declaration instructions are inserted into the instruction array instead. --- include/vkd3d_shader.h | 5 + libs/vkd3d-shader/dxbc.c | 1 + libs/vkd3d-shader/spirv.c | 1681 +++++++++++++--------- libs/vkd3d-shader/vkd3d_shader_private.h | 5 + tests/d3d12.c | 1 - 5 files changed, 1016 insertions(+), 677 deletions(-)
diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 86ffb9d5..3b106e68 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -1444,6 +1444,11 @@ struct vkd3d_shader_signature_element unsigned int used_mask; /** Minimum interpolation precision. */ enum vkd3d_shader_minimum_precision min_precision; + /** + * Register count. A value of 0 or 1 indicates \a register_index is the + * only register. Values greater than 4 are invalid. \since 1.7 + */ + unsigned int register_count; };
/** diff --git a/libs/vkd3d-shader/dxbc.c b/libs/vkd3d-shader/dxbc.c index 4041e0f5..2e78cdd3 100644 --- a/libs/vkd3d-shader/dxbc.c +++ b/libs/vkd3d-shader/dxbc.c @@ -1887,6 +1887,7 @@ static int shader_parse_signature(DWORD tag, const char *data, DWORD data_size, read_dword(&ptr, &mask); e[i].mask = mask & 0xff; e[i].used_mask = (mask >> 8) & 0xff; + e[i].register_count = 1; switch (tag) { case TAG_OSGN: diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 0f46c713..ea48dab5 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -1967,11 +1967,9 @@ struct vkd3d_symbol_register_data uint32_t member_idx; enum vkd3d_shader_component_type component_type; unsigned int write_mask; - uint32_t dcl_mask; unsigned int structure_stride; unsigned int binding_base_idx; bool is_aggregate; /* An aggregate, i.e. a structure or an array. */ - bool is_dynamically_indexed; /* If member_idx is a variable ID instead of a constant. */ };
struct vkd3d_symbol_resource_data @@ -2064,8 +2062,15 @@ static void vkd3d_symbol_make_register(struct vkd3d_symbol *symbol, symbol->type = VKD3D_SYMBOL_REGISTER; memset(&symbol->key, 0, sizeof(symbol->key)); symbol->key.reg.type = reg->type; - if (vkd3d_shader_register_is_input(reg) && reg->idx[1].offset != ~0u) - symbol->key.reg.idx = reg->idx[1].offset; + if (vkd3d_shader_register_is_input(reg) || vkd3d_shader_register_is_output(reg) + || vkd3d_shader_register_is_patch_constant(reg)) + { + unsigned int i; + for (i = 2; i > 0; --i) + if (reg->idx[i].offset != ~0u) + break; + symbol->key.reg.idx = reg->idx[i].offset; + } else if (reg->type != VKD3DSPR_IMMCONSTBUFFER) symbol->key.reg.idx = reg->idx[0].offset; } @@ -2080,11 +2085,9 @@ static void vkd3d_symbol_set_register_info(struct vkd3d_symbol *symbol, symbol->info.reg.member_idx = 0; symbol->info.reg.component_type = component_type; symbol->info.reg.write_mask = write_mask; - symbol->info.reg.dcl_mask = 0; symbol->info.reg.structure_stride = 0; symbol->info.reg.binding_base_idx = 0; symbol->info.reg.is_aggregate = false; - symbol->info.reg.is_dynamically_indexed = false; }
static void vkd3d_symbol_make_resource(struct vkd3d_symbol *symbol, @@ -2250,9 +2253,9 @@ struct spirv_compiler const struct vkd3d_shader_spirv_target_info *spirv_target_info;
bool after_declarations_section; - const struct vkd3d_shader_signature *input_signature; - const struct vkd3d_shader_signature *output_signature; - const struct vkd3d_shader_signature *patch_constant_signature; + struct vkd3d_shader_signature input_signature; + struct vkd3d_shader_signature output_signature; + struct vkd3d_shader_signature patch_constant_signature; const struct vkd3d_shader_transform_feedback_info *xfb_info; struct vkd3d_shader_output_info { @@ -2273,6 +2276,7 @@ struct spirv_compiler bool use_vocp;
enum vkd3d_shader_opcode phase; + bool emit_default_control_point_phase; struct vkd3d_shader_phase control_point_phase; struct vkd3d_shader_phase patch_constant_phase;
@@ -2408,9 +2412,9 @@ struct spirv_compiler *spirv_compiler_create(const struct vkd3d_shader_version *
compiler->shader_type = shader_version->type;
- compiler->input_signature = &shader_desc->input_signature; - compiler->output_signature = &shader_desc->output_signature; - compiler->patch_constant_signature = &shader_desc->patch_constant_signature; + compiler->input_signature = shader_desc->input_signature; + compiler->output_signature = shader_desc->output_signature; + compiler->patch_constant_signature = shader_desc->patch_constant_signature;
if ((shader_interface = vkd3d_find_struct(compile_info->next, INTERFACE_INFO))) { @@ -2967,18 +2971,27 @@ static uint32_t spirv_compiler_emit_variable(struct spirv_compiler *compiler,
static uint32_t spirv_compiler_emit_array_variable(struct spirv_compiler *compiler, struct vkd3d_spirv_stream *stream, SpvStorageClass storage_class, - enum vkd3d_shader_component_type component_type, unsigned int component_count, unsigned int array_length) + enum vkd3d_shader_component_type component_type, unsigned int component_count, unsigned int array_length, + unsigned int outer_array_length) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; uint32_t type_id, length_id, ptr_type_id;
- if (!array_length) + if (!array_length && !outer_array_length) return spirv_compiler_emit_variable(compiler, stream, storage_class, component_type, component_count);
type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count); - length_id = spirv_compiler_get_constant_uint(compiler, array_length); - type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); + if (array_length) + { + length_id = spirv_compiler_get_constant_uint(compiler, array_length); + type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); + } + if (outer_array_length) + { + length_id = spirv_compiler_get_constant_uint(compiler, outer_array_length); + type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); + } ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, storage_class, type_id); return vkd3d_spirv_build_op_variable(builder, stream, ptr_type_id, storage_class, 0); } @@ -3171,7 +3184,6 @@ struct vkd3d_shader_register_info unsigned int structure_stride; unsigned int binding_base_idx; bool is_aggregate; - bool is_dynamically_indexed; };
static bool spirv_compiler_get_register_info(const struct spirv_compiler *compiler, @@ -3194,7 +3206,6 @@ static bool spirv_compiler_get_register_info(const struct spirv_compiler *compil register_info->structure_stride = 0; register_info->binding_base_idx = 0; register_info->is_aggregate = false; - register_info->is_dynamically_indexed = false; return true; }
@@ -3216,7 +3227,6 @@ static bool spirv_compiler_get_register_info(const struct spirv_compiler *compil register_info->structure_stride = symbol->info.reg.structure_stride; register_info->binding_base_idx = symbol->info.reg.binding_base_idx; register_info->is_aggregate = symbol->info.reg.is_aggregate; - register_info->is_dynamically_indexed = symbol->info.reg.is_dynamically_indexed;
return true; } @@ -3346,33 +3356,14 @@ static void spirv_compiler_emit_dereference_register(struct spirv_compiler *comp } else if (register_info->is_aggregate) { - if (reg->type == VKD3DSPR_INPUT || reg->type == VKD3DSPR_INCONTROLPOINT) - { - /* Indices for these are swapped compared to the generated SPIR-V. */ - if (reg->idx[1].offset != ~0u) - indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, ®->idx[1]); - if (reg->idx[0].offset != ~0u) - indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, ®->idx[0]); - } - else - { - struct vkd3d_shader_register_index reg_idx = reg->idx[0]; - - if (reg->idx[1].rel_addr) - FIXME("Relative addressing not implemented.\n"); - - if (register_info->is_dynamically_indexed) - { - indexes[index_count++] = vkd3d_spirv_build_op_load(builder, - vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_INT, 1), - register_info->member_idx, SpvMemoryAccessMaskNone); - } - else - { - reg_idx.offset = register_info->member_idx; - indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, ®_idx); - } - } + /* Indices for these are swapped compared to the generated SPIR-V. */ + if (reg->idx[2].offset != ~0u) + indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, ®->idx[1]); + if (reg->idx[1].offset != ~0u) + indexes[index_count++] = spirv_compiler_emit_register_addressing(compiler, ®->idx[0]); + if (!index_count) + /* A register sysval which is an array in SPIR-V, e.g. SAMPLEMASK. */ + indexes[index_count++] = spirv_compiler_get_constant_uint(compiler, 0); } else { @@ -4251,35 +4242,12 @@ static const struct vkd3d_spirv_builtin *vkd3d_get_spirv_builtin(const struct sp if ((builtin = get_spirv_builtin_for_register(reg_type))) return builtin;
- if (sysval != VKD3D_SIV_NONE || (reg_type != VKD3DSPR_OUTPUT && reg_type != VKD3DSPR_COLOROUT)) + if (sysval != VKD3D_SIV_NONE || (reg_type != VKD3DSPR_OUTPUT && reg_type != VKD3DSPR_COLOROUT + && reg_type != VKD3DSPR_PATCHCONST)) FIXME("Unhandled builtin (register type %#x, sysval %#x).\n", reg_type, sysval); return NULL; }
-static const struct vkd3d_shader_signature_element *vkd3d_find_signature_element_for_reg( - const struct vkd3d_shader_signature *signature, unsigned int *signature_element_index, - unsigned int reg_idx, DWORD write_mask) -{ - unsigned int signature_idx; - - for (signature_idx = 0; signature_idx < signature->element_count; ++signature_idx) - { - if (signature->elements[signature_idx].register_index == reg_idx - && (signature->elements[signature_idx].mask & write_mask) == write_mask) - { - if (signature_element_index) - *signature_element_index = signature_idx; - return &signature->elements[signature_idx]; - } - } - - FIXME("Could not find shader signature element (register %u, write mask %#x).\n", - reg_idx, write_mask); - if (signature_element_index) - *signature_element_index = ~0u; - return NULL; -} - static uint32_t spirv_compiler_get_invocation_id(struct spirv_compiler *compiler) { struct vkd3d_shader_register r; @@ -4400,7 +4368,8 @@ static void spirv_compiler_decorate_xfb_output(struct spirv_compiler *compiler, }
static uint32_t spirv_compiler_emit_builtin_variable(struct spirv_compiler *compiler, - const struct vkd3d_spirv_builtin *builtin, SpvStorageClass storage_class, unsigned int array_size) + const struct vkd3d_spirv_builtin *builtin, SpvStorageClass storage_class, unsigned int array_size, + unsigned int outer_array_length) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; uint32_t id; @@ -4409,7 +4378,7 @@ static uint32_t spirv_compiler_emit_builtin_variable(struct spirv_compiler *comp
id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, storage_class, - builtin->component_type, builtin->component_count, array_size); + builtin->component_type, builtin->component_count, array_size, outer_array_length); vkd3d_spirv_add_iface_variable(builder, id); spirv_compiler_decorate_builtin(compiler, id, builtin->spirv_builtin);
@@ -4421,54 +4390,41 @@ static uint32_t spirv_compiler_emit_builtin_variable(struct spirv_compiler *comp return id; }
-static bool needs_private_io_variable(const struct vkd3d_shader_signature *signature, - unsigned int reg_idx, const struct vkd3d_spirv_builtin *builtin, - unsigned int *component_count, unsigned int *out_write_mask) +static bool needs_private_io_variable(const struct vkd3d_spirv_builtin *builtin) { - unsigned int write_mask = 0; - bool have_sysval = false; - unsigned int i, count; - - /* Always use private variables for arrayed builtins. These are generally - * scalars on the D3D side, so would need extra array indices when - * accessing them. It may be feasible to insert those indices at the point - * where the builtins are used, but it's not clear it's worth the effort. */ - if (builtin && (builtin->spirv_array_size || builtin->fixup_pfn)) - return true; - - if (*component_count == VKD3D_VEC4_SIZE) - return false; - - for (i = 0, count = 0; i < signature->element_count; ++i) - { - const struct vkd3d_shader_signature_element *current = &signature->elements[i]; - - if (current->register_index != reg_idx) - continue; + return builtin && builtin->fixup_pfn; +}
- write_mask |= current->mask; - ++count; +static unsigned int vkd3d_shader_signature_next_location(const struct vkd3d_shader_signature *signature) +{ + unsigned int i, max_row;
- if (current->sysval_semantic) - have_sysval = true; - } + if (!signature) + return 0;
- if (count == 1) - return false; + for (i = 0, max_row = 0; i < signature->element_count; ++i) + max_row = max(max_row, signature->elements[i].register_index + signature->elements[i].register_count); + return max_row; +}
- if (builtin || have_sysval) - return true; +static unsigned int shader_register_get_io_indices(const struct vkd3d_shader_register *reg, + unsigned int *array_size, unsigned int *outer_array_size) +{ + unsigned int i, element_idx;
- if (!vkd3d_bitmask_is_contiguous(write_mask)) + *outer_array_size = 0; + *array_size = 0; + element_idx = reg->idx[0].offset; + for (i = 1; i < 3; ++i) { - FIXME("Write mask %#x is non-contiguous.\n", write_mask); - return true; + if (reg->idx[i].offset == ~0u) + break; + *outer_array_size = *array_size; + *array_size = element_idx; + element_idx = reg->idx[i].offset; }
- assert(vkd3d_write_mask_component_count(write_mask) >= *component_count); - *component_count = vkd3d_write_mask_component_count(write_mask); - *out_write_mask = write_mask; - return false; + return element_idx; }
static uint32_t spirv_compiler_emit_input(struct spirv_compiler *compiler, @@ -4477,47 +4433,30 @@ static uint32_t spirv_compiler_emit_input(struct spirv_compiler *compiler, { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_shader_signature_element *signature_element; + unsigned int array_size, outer_array_size, element_idx; const struct vkd3d_shader_signature *shader_signature; const struct vkd3d_shader_register *reg = &dst->reg; unsigned int component_idx, input_component_count; enum vkd3d_shader_component_type component_type; uint32_t type_id, ptr_type_id, float_type_id; const struct vkd3d_spirv_builtin *builtin; + unsigned int write_mask, reg_write_mask; struct vkd3d_symbol *symbol = NULL; uint32_t val_id, input_id, var_id; struct vkd3d_symbol reg_symbol; - struct vkd3d_symbol tmp_symbol; SpvStorageClass storage_class; struct rb_entry *entry = NULL; bool use_private_var = false; - unsigned int write_mask; - unsigned int array_size; - unsigned int reg_idx; uint32_t i, index;
assert(!reg->idx[0].rel_addr); assert(!reg->idx[1].rel_addr);
- if (reg->idx[1].offset != ~0u) - { - array_size = reg->idx[0].offset; - reg_idx = reg->idx[1].offset; - } - else - { - array_size = 0; - reg_idx = reg->idx[0].offset; - } - shader_signature = reg->type == VKD3DSPR_PATCHCONST - ? compiler->patch_constant_signature : compiler->input_signature; + ? &compiler->patch_constant_signature : &compiler->input_signature;
- if (!(signature_element = vkd3d_find_signature_element_for_reg(shader_signature, - NULL, reg_idx, dst->write_mask))) - { - FIXME("No signature element for shader input, ignoring shader input.\n"); - return 0; - } + element_idx = shader_register_get_io_indices(reg, &array_size, &outer_array_size); + signature_element = &shader_signature->elements[element_idx];
if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL && !sysval && signature_element->sysval_semantic) sysval = vkd3d_siv_from_sysval(signature_element->sysval_semantic); @@ -4539,12 +4478,16 @@ static uint32_t spirv_compiler_emit_input(struct spirv_compiler *compiler, component_idx = vkd3d_write_mask_get_component_idx(signature_element->mask); }
- if (needs_private_io_variable(shader_signature, reg_idx, builtin, &input_component_count, &write_mask) - && (compiler->shader_type != VKD3D_SHADER_TYPE_HULL - || (reg->type != VKD3DSPR_INCONTROLPOINT && reg->type != VKD3DSPR_PATCHCONST))) + if (needs_private_io_variable(builtin)) + { use_private_var = true; + reg_write_mask = write_mask; + } else + { component_idx = vkd3d_write_mask_get_component_idx(write_mask); + reg_write_mask = write_mask >> component_idx; + }
storage_class = SpvStorageClassInput;
@@ -4552,101 +4495,59 @@ static uint32_t spirv_compiler_emit_input(struct spirv_compiler *compiler,
if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) { + /* Except for vicp there should be one declaration per signature element. Sources of + * duplicate declarations are: a single register split into multiple declarations having + * different components, which should have been merged, and declarations in one phase + * being repeated in another (i.e. vcp/vocp), which should have been deleted. */ + if (reg->type != VKD3DSPR_INPUT || !is_in_fork_or_join_phase(compiler)) + FIXME("Duplicate input definition found.\n"); symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - input_id = symbol->id; - } - else if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL - && (reg->type == VKD3DSPR_INCONTROLPOINT || reg->type == VKD3DSPR_PATCHCONST)) - { - /* Input/output registers from one phase can be used as inputs in - * subsequent phases. Specifically: - * - * - Control phase inputs are available as "vicp" in fork and join - * phases. - * - Control phase outputs are available as "vocp" in fork and join - * phases. - * - Fork phase patch constants are available as "vpc" in join - * phases. - * - * We handle "vicp" and "vpc" here by creating aliases to the shader's - * global inputs and outputs. We handle "vocp" in - * spirv_compiler_leave_shader_phase(). */ - - tmp_symbol = reg_symbol; - if (reg->type == VKD3DSPR_PATCHCONST) - tmp_symbol.key.reg.type = VKD3DSPR_OUTPUT; - else - tmp_symbol.key.reg.type = VKD3DSPR_INPUT; - - if ((entry = rb_get(&compiler->symbol_table, &tmp_symbol))) - { - symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - tmp_symbol = *symbol; - tmp_symbol.key.reg.type = reg->type; - spirv_compiler_put_symbol(compiler, &tmp_symbol); - - input_id = symbol->id; - } - else - { - if (reg->type == VKD3DSPR_PATCHCONST) - ERR("Patch constant register %u was not declared in a previous phase.\n", reg_idx); - else - ERR("Input control point register %u was not declared in a previous phase.\n", reg_idx); - } + return symbol->id; }
- if (!symbol || ~symbol->info.reg.dcl_mask & write_mask) + if (builtin) { - if (builtin) - { - input_id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size); - if (reg->type == VKD3DSPR_PATCHCONST) - vkd3d_spirv_build_op_decorate(builder, input_id, SpvDecorationPatch, NULL, 0); - } - else - { - unsigned int location = reg_idx; - - input_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - storage_class, component_type, input_component_count, array_size); - vkd3d_spirv_add_iface_variable(builder, input_id); - if (reg->type == VKD3DSPR_PATCHCONST) - { - vkd3d_spirv_build_op_decorate(builder, input_id, SpvDecorationPatch, NULL, 0); - location += compiler->input_signature->element_count; - } - vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationLocation, location); - if (component_idx) - vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationComponent, component_idx); - - spirv_compiler_emit_interpolation_decorations(compiler, input_id, interpolation_mode); - } + input_id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size, + outer_array_size); + if (reg->type == VKD3DSPR_PATCHCONST) + vkd3d_spirv_build_op_decorate(builder, input_id, SpvDecorationPatch, NULL, 0); } - - if (!symbol) + else { - var_id = input_id; - if (use_private_var) + unsigned int location = signature_element->register_index; + + input_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, + storage_class, component_type, input_component_count, array_size, outer_array_size); + vkd3d_spirv_add_iface_variable(builder, input_id); + if (reg->type == VKD3DSPR_PATCHCONST) { - storage_class = SpvStorageClassPrivate; - var_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - storage_class, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE, array_size); + vkd3d_spirv_build_op_decorate(builder, input_id, SpvDecorationPatch, NULL, 0); + location += vkd3d_shader_signature_next_location(&compiler->input_signature); } + vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationLocation, location); + if (component_idx) + vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationComponent, component_idx);
- vkd3d_symbol_set_register_info(®_symbol, var_id, storage_class, - use_private_var ? VKD3D_SHADER_COMPONENT_FLOAT : component_type, - use_private_var ? VKD3DSP_WRITEMASK_ALL : write_mask); - reg_symbol.info.reg.dcl_mask |= write_mask; - spirv_compiler_put_symbol(compiler, ®_symbol); - - spirv_compiler_emit_register_debug_name(builder, var_id, reg); + spirv_compiler_emit_interpolation_decorations(compiler, input_id, interpolation_mode); } - else + + var_id = input_id; + if (use_private_var) { - symbol->info.reg.dcl_mask |= write_mask; + storage_class = SpvStorageClassPrivate; + var_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, + storage_class, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE, array_size, outer_array_size); }
+ vkd3d_symbol_set_register_info(®_symbol, var_id, storage_class, + use_private_var ? VKD3D_SHADER_COMPONENT_FLOAT : component_type, + use_private_var ? VKD3DSP_WRITEMASK_ALL : reg_write_mask); + reg_symbol.info.reg.is_aggregate = array_size || outer_array_size; + assert(!builtin || !builtin->spirv_array_size || use_private_var || array_size || outer_array_size); + spirv_compiler_put_symbol(compiler, ®_symbol); + + spirv_compiler_emit_register_debug_name(builder, var_id, reg); + if (use_private_var) { type_id = vkd3d_spirv_get_type_id(builder, component_type, input_component_count); @@ -4671,7 +4572,7 @@ static uint32_t spirv_compiler_emit_input(struct spirv_compiler *compiler, ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassInput, type_id); index = spirv_compiler_get_constant_uint(compiler, builtin->member_idx); val_id = vkd3d_spirv_build_op_in_bounds_access_chain1(builder, ptr_type_id, input_id, index); - dst_reg.idx[0].offset = reg_idx + i; + dst_reg.idx[0].offset = element_idx + i; } val_id = vkd3d_spirv_build_op_load(builder, type_id, val_id, SpvMemoryAccessMaskNone);
@@ -4721,12 +4622,11 @@ static void spirv_compiler_emit_input_register(struct spirv_compiler *compiler, if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) return;
- input_id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassInput, 0); + input_id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassInput, 0, 0);
write_mask = vkd3d_write_mask_from_component_count(builtin->component_count); vkd3d_symbol_set_register_info(®_symbol, input_id, SpvStorageClassInput, builtin->component_type, write_mask); - reg_symbol.info.reg.dcl_mask = write_mask; reg_symbol.info.reg.is_aggregate = builtin->spirv_array_size; spirv_compiler_put_symbol(compiler, ®_symbol); spirv_compiler_emit_register_debug_name(builder, input_id, reg); @@ -4796,38 +4696,10 @@ static void calculate_clip_or_cull_distance_mask(const struct vkd3d_shader_signa *mask |= (e->mask & VKD3DSP_WRITEMASK_ALL) << (VKD3D_VEC4_SIZE * e->semantic_index); }
-static uint32_t calculate_sysval_array_mask(struct spirv_compiler *compiler, - const struct vkd3d_shader_signature *signature, enum vkd3d_shader_input_sysval_semantic sysval) -{ - const struct vkd3d_shader_signature_element *e; - const struct vkd3d_spirv_builtin *sig_builtin; - const struct vkd3d_spirv_builtin *builtin; - uint32_t signature_idx, mask = 0; - - if (!(builtin = get_spirv_builtin_for_sysval(compiler, sysval))) - { - FIXME("Unhandled sysval %#x.\n", sysval); - return 0; - } - - for (signature_idx = 0; signature_idx < signature->element_count; ++signature_idx) - { - e = &signature->elements[signature_idx]; - - sig_builtin = get_spirv_builtin_for_sysval(compiler, - vkd3d_siv_from_sysval_indexed(e->sysval_semantic, e->semantic_index)); - - if (sig_builtin && sig_builtin->spirv_builtin == builtin->spirv_builtin) - mask |= (e->mask & VKD3DSP_WRITEMASK_ALL) << (VKD3D_VEC4_SIZE * sig_builtin->member_idx); - } - - return mask; -} - /* Emits arrayed SPIR-V built-in variables. */ static void spirv_compiler_emit_shader_signature_outputs(struct spirv_compiler *compiler) { - const struct vkd3d_shader_signature *output_signature = compiler->output_signature; + const struct vkd3d_shader_signature *output_signature = &compiler->output_signature; uint32_t clip_distance_mask = 0, clip_distance_id = 0; uint32_t cull_distance_mask = 0, cull_distance_id = 0; const struct vkd3d_spirv_builtin *builtin; @@ -4857,7 +4729,7 @@ static void spirv_compiler_emit_shader_signature_outputs(struct spirv_compiler * count = vkd3d_popcount(clip_distance_mask); builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SIV_CLIP_DISTANCE); clip_distance_id = spirv_compiler_emit_builtin_variable(compiler, - builtin, SpvStorageClassOutput, count); + builtin, SpvStorageClassOutput, count, 0); }
if (cull_distance_mask) @@ -4865,7 +4737,7 @@ static void spirv_compiler_emit_shader_signature_outputs(struct spirv_compiler * count = vkd3d_popcount(cull_distance_mask); builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SIV_CULL_DISTANCE); cull_distance_id = spirv_compiler_emit_builtin_variable(compiler, - builtin, SpvStorageClassOutput, count); + builtin, SpvStorageClassOutput, count, 0); }
for (i = 0; i < output_signature->element_count; ++i) @@ -4912,13 +4784,12 @@ static void spirv_compiler_emit_output_register(struct spirv_compiler *compiler, return; }
- output_id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0); + output_id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0, 0);
vkd3d_symbol_make_register(®_symbol, reg); write_mask = vkd3d_write_mask_from_component_count(builtin->component_count); vkd3d_symbol_set_register_info(®_symbol, output_id, SpvStorageClassOutput, builtin->component_type, write_mask); - reg_symbol.info.reg.dcl_mask = write_mask; reg_symbol.info.reg.is_aggregate = builtin->spirv_array_size; spirv_compiler_put_symbol(compiler, ®_symbol); spirv_compiler_emit_register_execution_mode(compiler, reg); @@ -4941,7 +4812,7 @@ static uint32_t spirv_compiler_emit_shader_phase_builtin_variable(struct spirv_c if (variable_id && *variable_id) return *variable_id;
- id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0); + id = spirv_compiler_emit_builtin_variable(compiler, builtin, SpvStorageClassOutput, 0, 0); if (is_in_fork_or_join_phase(compiler)) vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0);
@@ -4960,36 +4831,28 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, unsigned int component_idx, output_component_count; enum vkd3d_shader_component_type component_type; const struct vkd3d_spirv_builtin *builtin; - struct vkd3d_symbol *symbol = NULL; + unsigned int array_size, outer_array_size; + unsigned int write_mask, reg_write_mask; bool use_private_variable = false; struct vkd3d_symbol reg_symbol; SpvStorageClass storage_class; - struct rb_entry *entry = NULL; - unsigned int signature_idx; - unsigned int write_mask; - unsigned int array_size; + unsigned int element_idx; bool is_patch_constant; uint32_t id, var_id;
is_patch_constant = is_in_fork_or_join_phase(compiler);
- shader_signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature; - - array_size = is_in_control_point_phase(compiler) ? compiler->output_control_point_count : 0; + shader_signature = is_patch_constant ? &compiler->patch_constant_signature : &compiler->output_signature;
- if (!(signature_element = vkd3d_find_signature_element_for_reg(shader_signature, - &signature_idx, reg->idx[0].offset, dst->write_mask))) - { - FIXME("No signature element for shader output, ignoring shader output.\n"); - return; - } + element_idx = shader_register_get_io_indices(reg, &array_size, &outer_array_size); + signature_element = &shader_signature->elements[element_idx];
builtin = vkd3d_get_spirv_builtin(compiler, dst->reg.type, sysval);
write_mask = signature_element->mask;
- component_idx = vkd3d_write_mask_get_component_idx(dst->write_mask); - output_component_count = vkd3d_write_mask_component_count(signature_element->mask); + component_idx = vkd3d_write_mask_get_component_idx(write_mask); + output_component_count = vkd3d_write_mask_component_count(write_mask); if (builtin) { component_type = builtin->component_type; @@ -5005,121 +4868,100 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, storage_class = SpvStorageClassOutput;
if (get_shader_output_swizzle(compiler, signature_element->register_index) != VKD3D_SHADER_NO_SWIZZLE - || needs_private_io_variable(shader_signature, signature_element->register_index, - builtin, &output_component_count, &write_mask) - || is_patch_constant) + || (compiler->output_info[element_idx].id && compiler->output_info[element_idx].array_element_mask) + || needs_private_io_variable(builtin)) + { use_private_variable = true; + reg_write_mask = write_mask; + } else + { component_idx = vkd3d_write_mask_get_component_idx(write_mask); + reg_write_mask = write_mask >> component_idx; + }
vkd3d_symbol_make_register(®_symbol, reg);
- if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) + if (rb_get(&compiler->symbol_table, ®_symbol)) { - symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - id = symbol->id; + /* See spirv_compiler_emit_input() for possible causes. */ + FIXME("Duplicate output definition found.\n"); + return; }
- if (!symbol || ~symbol->info.reg.dcl_mask & write_mask) + if (compiler->output_info[element_idx].id) { - if (compiler->output_info[signature_idx].id) - { - id = compiler->output_info[signature_idx].id; - if (compiler->output_info[signature_idx].array_element_mask) - use_private_variable = true; - } - else if (builtin) - { - if (spirv_compiler_get_current_shader_phase(compiler)) - id = spirv_compiler_emit_shader_phase_builtin_variable(compiler, builtin); - else - id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size); - - if (builtin->spirv_array_size) - compiler->output_info[signature_idx].array_element_mask = - calculate_sysval_array_mask(compiler, shader_signature, sysval); - - spirv_compiler_emit_register_execution_mode(compiler, &dst->reg); - } + id = compiler->output_info[element_idx].id; + } + else if (builtin) + { + if (spirv_compiler_get_current_shader_phase(compiler)) + id = spirv_compiler_emit_shader_phase_builtin_variable(compiler, builtin); else - { - unsigned int location = reg->idx[0].offset; - - if (is_patch_constant) - location += compiler->output_signature->element_count; - - id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - storage_class, component_type, output_component_count, array_size); - vkd3d_spirv_add_iface_variable(builder, id); - - if (is_dual_source_blending(compiler) && reg->idx[0].offset < 2) - { - vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, 0); - vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationIndex, reg->idx[0].offset); - } - else - { - vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, location); - } + id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size, outer_array_size);
- if (component_idx) - vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationComponent, component_idx); - } - - if (is_patch_constant) - vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0); - - spirv_compiler_decorate_xfb_output(compiler, id, output_component_count, signature_element); - - compiler->output_info[signature_idx].id = id; - compiler->output_info[signature_idx].component_type = component_type; + spirv_compiler_emit_register_execution_mode(compiler, &dst->reg); } - - if (!symbol) + else { - var_id = id; - if (use_private_variable) - storage_class = SpvStorageClassPrivate; + unsigned int location = signature_element->register_index; + if (is_patch_constant) - var_id = compiler->hs.patch_constants_id; - else if (use_private_variable) - var_id = spirv_compiler_emit_variable(compiler, &builder->global_stream, - storage_class, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE); + location += vkd3d_shader_signature_next_location(&compiler->output_signature);
- vkd3d_symbol_set_register_info(®_symbol, var_id, storage_class, - use_private_variable ? VKD3D_SHADER_COMPONENT_FLOAT : component_type, - use_private_variable ? VKD3DSP_WRITEMASK_ALL : write_mask); - reg_symbol.info.reg.is_aggregate = use_private_variable ? is_patch_constant : array_size; - if (!use_private_variable && is_in_control_point_phase(compiler)) + id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, + storage_class, component_type, output_component_count, array_size, outer_array_size); + vkd3d_spirv_add_iface_variable(builder, id); + + if (is_dual_source_blending(compiler) && signature_element->register_index < 2) { - reg_symbol.info.reg.member_idx = spirv_compiler_get_invocation_id(compiler); - reg_symbol.info.reg.is_dynamically_indexed = true; + vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, 0); + vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationIndex, signature_element->register_index); } - else if (is_patch_constant) + else { - reg_symbol.info.reg.member_idx = reg->idx[0].offset; + vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationLocation, location); } - reg_symbol.info.reg.dcl_mask = write_mask; - - spirv_compiler_put_symbol(compiler, ®_symbol);
- if (!is_patch_constant) - spirv_compiler_emit_register_debug_name(builder, var_id, reg); + if (component_idx) + vkd3d_spirv_build_op_decorate1(builder, id, SpvDecorationComponent, component_idx); } - else + + if (is_patch_constant) + vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0); + + spirv_compiler_decorate_xfb_output(compiler, id, output_component_count, signature_element); + + compiler->output_info[element_idx].id = id; + compiler->output_info[element_idx].component_type = component_type; + + var_id = id; + if (use_private_variable) { - symbol->info.reg.dcl_mask |= write_mask; - var_id = symbol->id; + storage_class = SpvStorageClassPrivate; + var_id = spirv_compiler_emit_variable(compiler, &builder->global_stream, + storage_class, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE); }
+ vkd3d_symbol_set_register_info(®_symbol, var_id, storage_class, + use_private_variable ? VKD3D_SHADER_COMPONENT_FLOAT : component_type, + use_private_variable ? VKD3DSP_WRITEMASK_ALL : reg_write_mask); + reg_symbol.info.reg.is_aggregate = array_size || outer_array_size; + assert(!builtin || !builtin->spirv_array_size || use_private_variable || array_size || outer_array_size); + + spirv_compiler_put_symbol(compiler, ®_symbol); + + if (!is_patch_constant) + spirv_compiler_emit_register_debug_name(builder, var_id, reg); + if (use_private_variable) { - unsigned int idx = spirv_compiler_get_output_variable_index(compiler, reg->idx[0].offset); + unsigned int idx = spirv_compiler_get_output_variable_index(compiler, element_idx); compiler->private_output_variable[idx] = var_id; compiler->private_output_variable_write_mask[idx] |= dst->write_mask; if (is_patch_constant) compiler->private_output_variable_array_idx[idx] = spirv_compiler_get_constant_uint( - compiler, reg->idx[0].offset); + compiler, element_idx); if (!compiler->epilogue_function_id) compiler->epilogue_function_id = vkd3d_spirv_alloc_id(builder); } @@ -5171,6 +5013,9 @@ static void spirv_compiler_emit_store_shader_output(struct spirv_compiler *compi use_mask |= element->used_mask; } } + index = vkd3d_write_mask_get_component_idx(output->mask); + dst_write_mask >>= index; + use_mask >>= index; write_mask &= dst_write_mask;
if (!write_mask) @@ -5254,7 +5099,7 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler *
is_patch_constant = is_in_fork_or_join_phase(compiler);
- signature = is_patch_constant ? compiler->patch_constant_signature : compiler->output_signature; + signature = is_patch_constant ? &compiler->patch_constant_signature : &compiler->output_signature;
function_id = compiler->epilogue_function_id;
@@ -5293,8 +5138,7 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler * if (!compiler->output_info[i].id) continue;
- variable_idx = spirv_compiler_get_output_variable_index(compiler, - signature->elements[i].register_index); + variable_idx = spirv_compiler_get_output_variable_index(compiler, i); if (!param_id[variable_idx]) continue;
@@ -5324,24 +5168,6 @@ static void spirv_compiler_emit_hull_shader_builtins(struct spirv_compiler *comp spirv_compiler_emit_input_register(compiler, &dst); }
-static void spirv_compiler_emit_hull_shader_patch_constants(struct spirv_compiler *compiler) -{ - const struct vkd3d_shader_signature *signature = compiler->patch_constant_signature; - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - uint32_t register_count = 0; - unsigned int signature_idx; - - for (signature_idx = 0; signature_idx < signature->element_count; ++signature_idx) - register_count = max(register_count, signature->elements[signature_idx].register_index + 1); - - if (!register_count) - return; - - compiler->hs.patch_constants_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - SpvStorageClassPrivate, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE, register_count); - vkd3d_spirv_build_op_name(builder, compiler->hs.patch_constants_id, "opc"); -} - static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *compiler) { const struct vkd3d_shader_transform_feedback_info *xfb_info = compiler->xfb_info; @@ -5355,7 +5181,6 @@ static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *comp case VKD3D_SHADER_TYPE_HULL: vkd3d_spirv_set_execution_model(builder, SpvExecutionModelTessellationControl); spirv_compiler_emit_hull_shader_builtins(compiler); - spirv_compiler_emit_hull_shader_patch_constants(compiler); break; case VKD3D_SHADER_TYPE_DOMAIN: vkd3d_spirv_set_execution_model(builder, SpvExecutionModelTessellationEvaluation); @@ -5384,8 +5209,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); - - spirv_compiler_emit_shader_signature_outputs(compiler); } }
@@ -5423,6 +5246,11 @@ static void spirv_compiler_emit_dcl_global_flags(struct spirv_compiler *compiler WARN("Unhandled global flags %#x.\n", flags); }
+static bool shader_instruction_is_fork_or_join_phase(const struct vkd3d_shader_instruction *ins) +{ + return ins->handler_idx == VKD3DSIH_HS_FORK_PHASE || ins->handler_idx == VKD3DSIH_HS_JOIN_PHASE; +} + static void spirv_compiler_emit_dcl_temps(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { @@ -5472,7 +5300,7 @@ static void spirv_compiler_emit_dcl_indexable_temp(struct spirv_compiler *compil vkd3d_spirv_begin_function_stream_insertion(builder, function_location);
id = spirv_compiler_emit_array_variable(compiler, &builder->function_stream, - SpvStorageClassFunction, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE, temp->register_size); + SpvStorageClassFunction, VKD3D_SHADER_COMPONENT_FLOAT, VKD3D_VEC4_SIZE, temp->register_size, 0);
spirv_compiler_emit_register_debug_name(builder, id, ®);
@@ -6168,7 +5996,8 @@ static void spirv_compiler_emit_dcl_output(struct spirv_compiler *compiler, { const struct vkd3d_shader_dst_param *dst = &instruction->declaration.dst;
- if (vkd3d_shader_register_is_output(&dst->reg)) + if (vkd3d_shader_register_is_output(&dst->reg) + || (is_in_fork_or_join_phase(compiler) && vkd3d_shader_register_is_patch_constant(&dst->reg))) spirv_compiler_emit_output(compiler, dst, VKD3D_SIV_NONE); else spirv_compiler_emit_output_register(compiler, dst); @@ -6186,64 +6015,6 @@ static void spirv_compiler_emit_dcl_output_siv(struct spirv_compiler *compiler, spirv_compiler_emit_output(compiler, dst, sysval); }
-static bool spirv_compiler_check_index_range(struct spirv_compiler *compiler, - const struct vkd3d_shader_index_range *range) -{ - const struct vkd3d_shader_register *reg = &range->dst.reg; - struct vkd3d_shader_register_info reg_info; - struct vkd3d_shader_register current_reg; - struct vkd3d_symbol reg_symbol; - unsigned int i; - uint32_t id; - - current_reg = *reg; - vkd3d_symbol_make_register(®_symbol, ¤t_reg); - if (!spirv_compiler_get_register_info(compiler, ¤t_reg, ®_info)) - { - ERR("Failed to get register info.\n"); - return false; - } - - /* FIXME: We should check if it's an array. */ - if (!reg_info.is_aggregate) - { - FIXME("Unhandled register %#x.\n", reg->type); - return false; - } - id = reg_info.id; - - for (i = reg->idx[0].offset; i < reg->idx[0].offset + range->register_count; ++i) - { - current_reg.idx[0].offset = i; - vkd3d_symbol_make_register(®_symbol, ¤t_reg); - - if (range->dst.write_mask != reg_info.write_mask - || vkd3d_write_mask_component_count(reg_info.write_mask) != 1) - { - FIXME("Unhandled index range write mask %#x (%#x).\n", - range->dst.write_mask, reg_info.write_mask); - return false; - } - - if (reg_info.id != id) - { - FIXME("Unhandled index range %#x, %u.\n", reg->type, i); - return false; - } - } - - return true; -} - -static void spirv_compiler_emit_dcl_index_range(struct spirv_compiler *compiler, - const struct vkd3d_shader_instruction *instruction) -{ - const struct vkd3d_shader_index_range *range = &instruction->declaration.index_range; - - if (!spirv_compiler_check_index_range(compiler, range)) - FIXME("Ignoring dcl_index_range %#x %u.\n", range->dst.reg.type, range->register_count); -} - static void spirv_compiler_emit_dcl_stream(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { @@ -6307,7 +6078,7 @@ static void spirv_compiler_emit_point_size(struct spirv_compiler *compiler) || compiler->write_tess_geom_point_size) { vkd3d_spirv_build_op_store(&compiler->spirv_builder, - spirv_compiler_emit_builtin_variable(compiler, &point_size, SpvStorageClassOutput, 0), + spirv_compiler_emit_builtin_variable(compiler, &point_size, SpvStorageClassOutput, 0, 0), spirv_compiler_get_constant_float(compiler, 1.0f), SpvMemoryAccessMaskNone); } } @@ -6439,25 +6210,21 @@ static void spirv_compiler_emit_dcl_thread_group(struct spirv_compiler *compiler SpvExecutionModeLocalSize, local_size, ARRAY_SIZE(local_size)); }
+static void spirv_compiler_emit_default_control_point_phase(struct spirv_compiler *compiler); + static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler) { - const struct vkd3d_shader_signature *signature = compiler->output_signature; + const struct vkd3d_shader_signature *signature = &compiler->output_signature; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - struct vkd3d_symbol reg_symbol, *symbol; - struct vkd3d_shader_register reg; - struct rb_entry *entry; - unsigned int i; + + if (is_in_control_point_phase(compiler) && compiler->emit_default_control_point_phase) + spirv_compiler_emit_default_control_point_phase(compiler);
vkd3d_spirv_build_op_function_end(builder);
compiler->temp_id = 0; compiler->temp_count = 0;
- /* - * vocp inputs in fork and join shader phases are outputs of the control - * point phase. Reinsert symbols for vocp registers while leaving the - * control point phase. - */ if (is_in_control_point_phase(compiler)) { if (compiler->epilogue_function_id) @@ -6466,44 +6233,14 @@ static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler) spirv_compiler_emit_shader_epilogue_function(compiler); }
- memset(®, 0, sizeof(reg)); - reg.idx[1].offset = ~0u; - /* Fork and join phases share output registers (patch constants). * Control point phase has separate output registers. */ memset(compiler->output_info, 0, signature->element_count * sizeof(*compiler->output_info)); memset(compiler->private_output_variable, 0, sizeof(compiler->private_output_variable)); memset(compiler->private_output_variable_array_idx, 0, sizeof(compiler->private_output_variable_array_idx)); memset(compiler->private_output_variable_write_mask, 0, sizeof(compiler->private_output_variable_write_mask)); - - for (i = 0; i < signature->element_count; ++i) - { - const struct vkd3d_shader_signature_element *e = &signature->elements[i]; - - reg.type = VKD3DSPR_OUTPUT; - reg.idx[0].offset = e->register_index; - vkd3d_symbol_make_register(®_symbol, ®); - if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) - { - rb_remove(&compiler->symbol_table, entry); - - symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - - reg.type = VKD3DSPR_OUTCONTROLPOINT; - reg.idx[1].offset = reg.idx[0].offset; - reg.idx[0].offset = compiler->output_control_point_count; - vkd3d_symbol_make_register(symbol, ®); - symbol->info.reg.is_aggregate = false; - - if (rb_put(&compiler->symbol_table, symbol, entry) == -1) - { - ERR("Failed to insert vocp symbol entry (%s).\n", debug_vkd3d_symbol(symbol)); - vkd3d_symbol_free(entry, NULL); - } - } - } - } -} + } +}
static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) @@ -6535,68 +6272,73 @@ static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler, ? &compiler->control_point_phase : &compiler->patch_constant_phase; phase->function_id = function_id; phase->function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); + + if (instruction->handler_idx == VKD3DSIH_HS_CONTROL_POINT_PHASE) + compiler->emit_default_control_point_phase = instruction->flags; }
static void spirv_compiler_emit_default_control_point_phase(struct spirv_compiler *compiler) { - const struct vkd3d_shader_signature *output_signature = compiler->output_signature; - const struct vkd3d_shader_signature *input_signature = compiler->input_signature; + const struct vkd3d_shader_signature *output_signature = &compiler->output_signature; + const struct vkd3d_shader_signature *input_signature = &compiler->input_signature; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; + uint32_t type_id, input_ptr_type_id, output_ptr_type_id; enum vkd3d_shader_component_type component_type; uint32_t input_id, output_id, dst_id, src_id; - struct vkd3d_shader_src_param invocation; + unsigned int component_count, array_size; struct vkd3d_shader_register input_reg; - uint32_t type_id, output_ptr_type_id; - unsigned int component_count; uint32_t invocation_id; unsigned int i;
invocation_id = spirv_compiler_emit_load_invocation_id(compiler);
- memset(&invocation, 0, sizeof(invocation)); - invocation.reg.type = VKD3DSPR_OUTPOINTID; - invocation.reg.data_type = VKD3D_DATA_INT; - invocation.reg.idx[0].offset = ~0u; - invocation.reg.idx[1].offset = ~0u; - invocation.reg.idx[2].offset = ~0u; - invocation.swizzle = VKD3D_SHADER_NO_SWIZZLE; - memset(&input_reg, 0, sizeof(input_reg)); input_reg.type = VKD3DSPR_INPUT; input_reg.data_type = VKD3D_DATA_FLOAT; - input_reg.idx[0].rel_addr = &invocation; + input_reg.idx[1].offset = ~0u; input_reg.idx[2].offset = ~0u; - input_id = spirv_compiler_get_register_id(compiler, &input_reg);
assert(input_signature->element_count == output_signature->element_count); for (i = 0; i < output_signature->element_count; ++i) { const struct vkd3d_shader_signature_element *output = &output_signature->elements[i]; const struct vkd3d_shader_signature_element *input = &input_signature->elements[i]; + struct vkd3d_shader_register_info register_info;
assert(input->mask == output->mask); assert(input->component_type == output->component_type);
- input_reg.idx[1].offset = input->register_index; - input_id = spirv_compiler_get_register_id(compiler, &input_reg); - type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_FLOAT, 4); - src_id = vkd3d_spirv_build_op_load(builder, type_id, input_id, SpvMemoryAccessMaskNone); + input_reg.idx[0].offset = i; + if (!spirv_compiler_get_register_info(compiler, &input_reg, ®ister_info)) + return; + + input_id = register_info.id; + + component_type = input->component_type; + component_count = vkd3d_write_mask_component_count(input->mask); + + type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count); + array_size = (input->register_count > 1) ? input->register_count : 0; + if (array_size) + type_id = vkd3d_spirv_get_op_type_array(builder, type_id, spirv_compiler_get_constant_uint(compiler, + array_size)); + + input_ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassInput, type_id); + src_id = vkd3d_spirv_build_op_access_chain1(builder, input_ptr_type_id, input_id, invocation_id);
- component_type = output->component_type; - component_count = vkd3d_write_mask_component_count(output->mask); output_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - SpvStorageClassOutput, component_type, component_count, compiler->output_control_point_count); + SpvStorageClassOutput, component_type, component_count, array_size, compiler->output_control_point_count); vkd3d_spirv_add_iface_variable(builder, output_id); vkd3d_spirv_build_op_decorate1(builder, output_id, SpvDecorationLocation, output->register_index); vkd3d_spirv_build_op_name(builder, output_id, "vocp%u", output->register_index);
- type_id = vkd3d_spirv_get_type_id(builder, component_type, component_count); output_ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassOutput, type_id); dst_id = vkd3d_spirv_build_op_access_chain1(builder, output_ptr_type_id, output_id, invocation_id);
- spirv_compiler_emit_store(compiler, dst_id, output->mask, - component_type, SpvStorageClassOutput, VKD3DSP_WRITEMASK_ALL, src_id); + vkd3d_spirv_build_op_copy_memory(builder, dst_id, src_id, SpvMemoryAccessMaskNone); } + + vkd3d_spirv_build_op_return(builder); }
static void spirv_compiler_emit_barrier(struct spirv_compiler *compiler, @@ -6625,95 +6367,6 @@ static void spirv_compiler_emit_hull_shader_barrier(struct spirv_compiler *compi SpvScopeWorkgroup, SpvScopeInvocation, SpvMemorySemanticsMaskNone); }
-static void spirv_compiler_emit_hull_shader_input_initialisation(struct spirv_compiler *compiler) -{ - uint32_t type_id, length_id, register_index_id, src_array_id, dst_array_id, vicp_id, tmp_id; - const struct vkd3d_shader_signature *signature = compiler->input_signature; - uint32_t src_type_id, dst_type_id, src_id, dst_id, point_index_id; - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - const struct vkd3d_shader_signature_element *element; - enum vkd3d_shader_input_sysval_semantic sysval; - const struct vkd3d_spirv_builtin *builtin; - struct vkd3d_symbol *symbol, symbol_key; - unsigned int register_count, i, j; - struct vkd3d_shader_register r; - struct rb_entry *entry; - uint32_t indices[2]; - - for (i = 0, register_count = 0; i < signature->element_count; ++i) - { - register_count = max(register_count, signature->elements[i].register_index + 1); - } - - type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_FLOAT, 4); - length_id = spirv_compiler_get_constant_uint(compiler, compiler->input_control_point_count); - type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); - type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPrivate, type_id); - - memset(&r, 0, sizeof(r)); - r.type = VKD3DSPR_INPUT; - r.idx[0].offset = 0; - r.idx[1].offset = ~0u; - vkd3d_symbol_make_register(&symbol_key, &r); - - for (i = 0; i < signature->element_count; ++i) - { - element = &signature->elements[i]; - - symbol_key.key.reg.idx = element->register_index; - entry = rb_get(&compiler->symbol_table, &symbol_key); - symbol = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - - vicp_id = symbol->id; - register_index_id = spirv_compiler_get_constant_uint(compiler, element->register_index); - dst_array_id = vkd3d_spirv_build_op_in_bounds_access_chain1(builder, type_id, vicp_id, register_index_id); - - if (element->sysval_semantic) - { - sysval = vkd3d_siv_from_sysval(element->sysval_semantic); - builtin = get_spirv_builtin_for_sysval(compiler, sysval); - src_array_id = spirv_compiler_emit_builtin_variable(compiler, builtin, - SpvStorageClassInput, compiler->input_control_point_count); - - if (builtin->component_count == 4) - { - vkd3d_spirv_build_op_copy_memory(builder, dst_array_id, src_array_id, SpvMemoryAccessMaskNone); - } - else - { - tmp_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_FLOAT, builtin->component_count); - src_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassInput, tmp_id); - dst_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPrivate, tmp_id); - - for (j = 0; j < compiler->input_control_point_count; ++j) - { - point_index_id = spirv_compiler_get_constant_uint(compiler, j); - src_id = vkd3d_spirv_build_op_in_bounds_access_chain1(builder, - src_type_id, src_array_id, point_index_id); - - indices[0] = point_index_id; - indices[1] = spirv_compiler_get_constant_uint(compiler, 0); - dst_id = vkd3d_spirv_build_op_in_bounds_access_chain(builder, - dst_type_id, dst_array_id, indices, 2); - - vkd3d_spirv_build_op_copy_memory(builder, dst_id, src_id, SpvMemoryAccessMaskNone); - } - } - } - else - { - src_array_id = spirv_compiler_emit_array_variable(compiler, &builder->global_stream, - SpvStorageClassInput, VKD3D_SHADER_COMPONENT_FLOAT, 4, compiler->input_control_point_count); - vkd3d_spirv_add_iface_variable(builder, src_array_id); - vkd3d_spirv_build_op_decorate1(builder, src_array_id, SpvDecorationLocation, element->register_index); - vkd3d_spirv_build_op_name(builder, src_array_id, "v%u", element->register_index); - - vkd3d_spirv_build_op_copy_memory(builder, dst_array_id, src_array_id, SpvMemoryAccessMaskNone); - } - symbol->info.reg.dcl_mask |= element->mask; - } -} - static void spirv_compiler_emit_shader_epilogue_invocation(struct spirv_compiler *compiler) { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; @@ -6760,14 +6413,9 @@ static void spirv_compiler_emit_hull_shader_main(struct spirv_compiler *compiler
vkd3d_spirv_builder_begin_main_function(builder);
- spirv_compiler_emit_hull_shader_input_initialisation(compiler); - void_id = vkd3d_spirv_get_op_type_void(builder);
- if (compiler->control_point_phase.function_id) - vkd3d_spirv_build_op_function_call(builder, void_id, compiler->control_point_phase.function_id, NULL, 0); - else - spirv_compiler_emit_default_control_point_phase(compiler); + vkd3d_spirv_build_op_function_call(builder, void_id, compiler->control_point_phase.function_id, NULL, 0);
/* TODO: only call the patch constant function for invocation 0. The simplest way * is to avoid use of private variables there, otherwise we would need a separate @@ -9401,58 +9049,6 @@ static void spirv_compiler_emit_cut_stream(struct spirv_compiler *compiler, vkd3d_spirv_build_op_end_primitive(builder); }
-static void spirv_compiler_emit_hull_shader_inputs(struct spirv_compiler *compiler) -{ - const struct vkd3d_shader_signature *signature = compiler->input_signature; - struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; - uint32_t type_id, length_id, vicp_id, vicp_type_id; - unsigned int register_count, register_idx, i; - struct vkd3d_shader_register r; - struct vkd3d_symbol symbol; - struct rb_entry *entry; - - for (i = 0, register_count = 0; i < signature->element_count; ++i) - { - register_count = max(register_count, signature->elements[i].register_index + 1); - } - - type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_FLOAT, 4); - length_id = spirv_compiler_get_constant_uint(compiler, compiler->input_control_point_count); - type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); - length_id = spirv_compiler_get_constant_uint(compiler, register_count); - type_id = vkd3d_spirv_get_op_type_array(builder, type_id, length_id); - vicp_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassPrivate, type_id); - - vicp_id = vkd3d_spirv_build_op_variable(builder, - &builder->global_stream, vicp_type_id, SpvStorageClassPrivate, 0); - vkd3d_spirv_build_op_name(builder, vicp_id, "vicp"); - - memset(&r, 0, sizeof(r)); - r.type = VKD3DSPR_INPUT; - r.idx[0].offset = 0; - r.idx[1].offset = ~0u; - vkd3d_symbol_make_register(&symbol, &r); - - for (i = 0; i < signature->element_count; ++i) - { - register_idx = signature->elements[i].register_index; - - symbol.key.reg.idx = register_idx; - if ((entry = rb_get(&compiler->symbol_table, &symbol))) - { - struct vkd3d_symbol *s = RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); - s->info.reg.dcl_mask |= signature->elements[i].mask; - continue; - } - - vkd3d_symbol_set_register_info(&symbol, vicp_id, SpvStorageClassPrivate, - VKD3D_SHADER_COMPONENT_FLOAT, VKD3DSP_WRITEMASK_ALL); - symbol.info.reg.dcl_mask = signature->elements[i].mask; - symbol.info.reg.is_aggregate = true; - spirv_compiler_put_symbol(compiler, &symbol); - } -} - /* This function is called after declarations are processed. */ static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler) { @@ -9461,8 +9057,6 @@ static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler) if (compiler->xfb_info && compiler->xfb_info->element_count && compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY) spirv_compiler_emit_point_size(compiler); - if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) - spirv_compiler_emit_hull_shader_inputs(compiler); }
static bool is_dcl_instruction(enum vkd3d_shader_opcode handler_idx) @@ -9540,9 +9134,6 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, case VKD3DSIH_DCL_OUTPUT_SIV: spirv_compiler_emit_dcl_output_siv(compiler, instruction); break; - case VKD3DSIH_DCL_INDEX_RANGE: - spirv_compiler_emit_dcl_index_range(compiler, instruction); - break; case VKD3DSIH_DCL_STREAM: spirv_compiler_emit_dcl_stream(compiler, instruction); break; @@ -9834,6 +9425,18 @@ struct vkd3d_shader_normaliser unsigned int instance_count; unsigned int phase_body_idx; enum vkd3d_shader_opcode phase; + bool has_control_point_phase; + unsigned int input_control_point_count; + unsigned int output_control_point_count; + + struct vkd3d_shader_src_param *outpointid_param; + + struct vkd3d_shader_dst_param *input_dcl_params[MAX_REG_OUTPUT]; + struct vkd3d_shader_dst_param *output_dcl_params[MAX_REG_OUTPUT]; + struct vkd3d_shader_dst_param *pc_dcl_params[MAX_REG_OUTPUT]; + uint8_t input_range_map[MAX_REG_OUTPUT][VKD3D_VEC4_SIZE]; + uint8_t output_range_map[MAX_REG_OUTPUT][VKD3D_VEC4_SIZE]; + uint8_t pc_range_map[MAX_REG_OUTPUT][VKD3D_VEC4_SIZE]; };
static inline bool shader_normaliser_new_instructions(struct vkd3d_shader_normaliser *normaliser, unsigned int extra) @@ -9964,13 +9567,30 @@ static bool shader_instruction_array_clone_instruction(struct vkd3d_shader_instr return true; }
+static bool normaliser_is_in_control_point_phase(const struct vkd3d_shader_normaliser *normaliser) +{ + return normaliser->phase == VKD3DSIH_HS_CONTROL_POINT_PHASE; +} + +static bool normaliser_is_in_fork_or_join_phase(const struct vkd3d_shader_normaliser *normaliser) +{ + return normaliser->phase == VKD3DSIH_HS_FORK_PHASE || normaliser->phase == VKD3DSIH_HS_JOIN_PHASE; +} + static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vkd3d_shader_normaliser *normaliser, unsigned int *instruction_count) { struct vkd3d_shader_instruction *ins = &normaliser->instructions.elements[normaliser->instructions.count]; + struct vkd3d_shader_register *reg = &ins->declaration.dst.reg; unsigned int i, j, count;
- if (ins->handler_idx == VKD3DSIH_HS_FORK_PHASE || ins->handler_idx == VKD3DSIH_HS_JOIN_PHASE) + if (ins->handler_idx == VKD3DSIH_HS_CONTROL_POINT_PHASE) + { + normaliser->phase = ins->handler_idx; + ins->flags = 0; + return VKD3D_OK; + } + else if (shader_instruction_is_fork_or_join_phase(ins)) { /* Leave the first occurrence and delete the rest. */ *instruction_count = normaliser->phase != ins->handler_idx; @@ -9988,13 +9608,16 @@ static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vk *instruction_count = 0; return VKD3D_OK; } - else if (ins->handler_idx == VKD3DSIH_DCL_INPUT && vkd3d_shader_register_is_phase_instance_id( - &ins->declaration.dst.reg)) + else if ((ins->handler_idx == VKD3DSIH_DCL_INPUT && vkd3d_shader_register_is_phase_instance_id(reg))) { *instruction_count = 0; return VKD3D_OK; } - else if (ins->handler_idx == VKD3DSIH_DCL_TEMPS && normaliser->phase != VKD3DSIH_INVALID) + + if (!normaliser_is_in_fork_or_join_phase(normaliser)) + return VKD3D_OK; + + if (ins->handler_idx == VKD3DSIH_DCL_TEMPS) { /* Leave only the first temp declaration and set it to the max count later. */ if (!normaliser->max_temp_count) @@ -10005,8 +9628,43 @@ static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vk return VKD3D_OK; }
- if (normaliser->phase == VKD3DSIH_INVALID || is_dcl_instruction(ins->handler_idx)) + if (is_dcl_instruction(ins->handler_idx)) + { + if (ins->handler_idx == VKD3DSIH_DCL_INPUT) + { + /* We don't need to keep OUTCONTROLPOINT or PATCHCONST input declarations since their + * equivalents were declared earlier, but INCONTROLPOINT can be the first appearance. */ + if (reg->type == VKD3DSPR_OUTCONTROLPOINT || reg->type == VKD3DSPR_PATCHCONST) + *instruction_count = 0; + else if (reg->type == VKD3DSPR_INCONTROLPOINT) + reg->type = VKD3DSPR_INPUT; + } return VKD3D_OK; + } + + /* Input/output registers from one phase can be used as inputs in + * subsequent phases. Specifically: + * + * - Control phase inputs are available as "vicp" in fork and join + * phases. + * - Control phase outputs are available as "vocp" in fork and join + * phases. + * - Fork phase patch constants are available as "vpc" in join + * phases. + * + * We handle "vicp" here by converting INCONTROLPOINT src registers to + * type INPUT so they match the control phase declarations. We handle + * "vocp" by converting OUTCONTROLPOINT registers to type OUTPUT. + * Merging fork and join phases handles "vpc". */ + + for (i = 0; i < ins->src_count; ++i) + { + struct vkd3d_shader_register *reg = (struct vkd3d_shader_register *)&ins->src[i].reg; + if (reg->type == VKD3DSPR_INCONTROLPOINT) + reg->type = VKD3DSPR_INPUT; + else if (reg->type == VKD3DSPR_OUTCONTROLPOINT) + reg->type = VKD3DSPR_OUTPUT; + }
if (ins->handler_idx == VKD3DSIH_MOV && vkd3d_shader_register_is_phase_instance_id(&ins->src->reg)) { @@ -10034,7 +9692,7 @@ static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vk
count = normaliser->instructions.count - normaliser->phase_body_idx;
- if (!shader_instruction_array_reserve(&normaliser->instructions, count * (normaliser->instance_count - 1))) + if (!shader_normaliser_new_instructions(normaliser, count * normaliser->instance_count)) return VKD3D_ERROR_OUT_OF_MEMORY;
/* Make a copy of the non-dcl instructions for each instance. */ @@ -10060,18 +9718,633 @@ static enum vkd3d_result shader_normaliser_eliminate_phase_instance_id(struct vk return VKD3D_OK; }
+static unsigned int shader_signature_find_element_for_reg(const struct vkd3d_shader_signature *signature, + unsigned int reg_idx, unsigned int write_mask) +{ + unsigned int signature_idx; + + for (signature_idx = 0; signature_idx < signature->element_count; ++signature_idx) + { + struct vkd3d_shader_signature_element *e = &signature->elements[signature_idx]; + if (e->register_index <= reg_idx && e->register_index + e->register_count > reg_idx + && (e->mask & write_mask) == write_mask) + { + return signature_idx; + } + } + + FIXME("Could not find shader signature element (register %u, write mask %#x).\n", + reg_idx, write_mask); + return ~0u; +} + +static inline unsigned int range_map_get_register_count(uint8_t range_map[][VKD3D_VEC4_SIZE], + unsigned int register_idx, unsigned int write_mask) +{ + if (register_idx >= MAX_REG_OUTPUT) + { + FIXME("Unhandled register index %#x\n", register_idx); + return 0; + } + return range_map[register_idx][vkd3d_write_mask_get_component_idx(write_mask)]; +} + +static void range_map_set_register_range(uint8_t range_map[][VKD3D_VEC4_SIZE], unsigned int register_idx, + unsigned int register_count, unsigned int write_mask, bool is_declaration) +{ + unsigned int i, j, r, c, component_idx, component_count; + + assert(write_mask <= VKD3DSP_WRITEMASK_ALL); + component_idx = vkd3d_write_mask_get_component_idx(write_mask); + component_count = vkd3d_write_mask_component_count(write_mask); + + if (write_mask > (1u << (component_idx + component_count)) - 1) + FIXME("Mask is not contiguous.\n"); + + if (register_idx >= MAX_REG_OUTPUT) + { + FIXME("Unhandled register index %u.\n", register_idx); + return; + } + if (MAX_REG_OUTPUT - register_idx < register_count) + FIXME("Unhandled register index %u.\n", MAX_REG_OUTPUT); + + if (range_map[register_idx][component_idx] > register_count) + { + if (range_map[register_idx][component_idx] == UINT8_MAX) + /* Ranges in different dcl_index_range instructions should not overlap from upstream. */ + FIXME("Register %u, component %u belongs to an upstream range.\n", register_idx, component_idx); + return; + } + if (range_map[register_idx][component_idx] == register_count) + { + /* Already done. This happens when fxc splits a register declaration by + * component(s). The dcl_index_range instructions are split too. */ + return; + } + range_map[register_idx][component_idx] = register_count; + + for (i = 0; i < min(register_count, MAX_REG_OUTPUT - register_idx); ++i) + { + r = register_idx + i; + for (j = !i; j < component_count; ++j) + { + c = component_idx + j; + /* A synthetic patch constant range which overlaps an existing range should not start upstream of it. + * This can happen for fork/join phase instancing, but we normalise that away, and fxc won't compile + * any other kind of dynamic indexing for tess factors. To support that we'd need to add a constant + * offset to the relative addresses. */ + if (range_map[r][c] && (is_declaration || (range_map[r][c] != UINT8_MAX))) + WARN("Register %u, component %u belongs to a different range.\n", r, c); + range_map[r][c] = UINT8_MAX; + } + } +} + +static void shader_normaliser_add_index_range(struct vkd3d_shader_normaliser *normaliser, + const struct vkd3d_shader_instruction *ins, struct spirv_compiler *compiler) +{ + const struct vkd3d_shader_index_range *range = &ins->declaration.index_range; + const struct vkd3d_shader_register *reg = &range->dst.reg; + const struct vkd3d_shader_signature *signature; + uint8_t (*range_map)[VKD3D_VEC4_SIZE]; + unsigned int reg_idx, element_idx; + + switch (reg->type) + { + case VKD3DSPR_INPUT: + case VKD3DSPR_INCONTROLPOINT: + range_map = normaliser->input_range_map; + signature = &compiler->input_signature; + break; + case VKD3DSPR_OUTCONTROLPOINT: + range_map = normaliser->output_range_map; + signature = &compiler->output_signature; + break; + case VKD3DSPR_OUTPUT: + if (!normaliser_is_in_fork_or_join_phase(normaliser)) + { + range_map = normaliser->output_range_map; + signature = &compiler->output_signature; + break; + } + /* fall through */ + case VKD3DSPR_PATCHCONST: + range_map = normaliser->pc_range_map; + signature = &compiler->patch_constant_signature; + break; + default: + FIXME("Unhandled register type %#x.\n", reg->type); + return; + } + + reg_idx = (reg->idx[1].offset != ~0u) ? reg->idx[1].offset : reg->idx[0].offset; + element_idx = shader_signature_find_element_for_reg(signature, reg_idx, range->dst.write_mask); + if (element_idx == ~0u) + { + FIXME("Failed to find signature element for register %u.\n", reg_idx); + return; + } + + range_map_set_register_range(range_map, reg_idx, range->register_count, + signature->elements[element_idx].mask, true); +} + +static int signature_element_mask_compare(const void *a, const void *b) +{ + const struct vkd3d_shader_signature_element *e = a, *f = b; + + if (e->mask == f->mask) + return e->register_index - f->register_index; + return e->mask - f->mask; +} + +static bool merge_sysval_semantics(struct vkd3d_shader_signature_element *e, + struct vkd3d_shader_signature_element *f) +{ + if (e->sysval_semantic < VKD3D_SHADER_SV_TESS_FACTOR_QUADEDGE + || e->sysval_semantic > VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN) + return false; + + return e->sysval_semantic == f->sysval_semantic + /* Line detail and density must be merged together to match the SPIR-V array. + * This deletes one of the two sysvals, but these are not used. */ + || (e->sysval_semantic == VKD3D_SHADER_SV_TESS_FACTOR_LINEDET + && f->sysval_semantic == VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN) + || (e->sysval_semantic == VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN + && f->sysval_semantic == VKD3D_SHADER_SV_TESS_FACTOR_LINEDET); +} + +/* Merge tess factor sysvals because they are an array in SPIR-V. */ +static void shader_signature_map_patch_constant_index_ranges(struct vkd3d_shader_signature *s, + uint8_t range_map[][VKD3D_VEC4_SIZE]) +{ + struct vkd3d_shader_signature_element *e, *f; + unsigned int i, j, register_count; + + qsort(s->elements, s->element_count, sizeof(s->elements[0]), signature_element_mask_compare); + + for (i = 0; i < s->element_count; i += register_count) + { + e = &s->elements[i]; + register_count = 1; + + if (!e->sysval_semantic) + continue; + + for (j = i + 1; j < s->element_count; ++j, ++register_count) + { + f = &s->elements[j]; + if (f->register_index != e->register_index + register_count || !merge_sysval_semantics(e, f)) + break; + } + if (register_count < 2) + continue; + + range_map_set_register_range(range_map, e->register_index, register_count, e->mask, false); + } +} + +static int signature_element_register_compare(const void *a, const void *b) +{ + const struct vkd3d_shader_signature_element *e = a, *f = b; + + return e->register_index - f->register_index; +} + +static int signature_element_index_compare(const void *a, const void *b) +{ + const struct vkd3d_shader_signature_element *e = a, *f = b; + + return e->stream_index - f->stream_index; +} + +static bool shader_signature_merge(struct vkd3d_shader_signature *s, uint8_t range_map[][VKD3D_VEC4_SIZE], + bool is_patch_constant) +{ + unsigned int i, j, element_count, new_count, register_count; + struct vkd3d_shader_signature_element *elements; + struct vkd3d_shader_signature_element *e, *f; + + element_count = s->element_count; + if (!(elements = vkd3d_malloc(element_count * sizeof(*elements)))) + return false; + memcpy(elements, s->elements, element_count * sizeof(*elements)); + + for (i = 0; i < element_count; ++i) + { + assert(elements[i].stream_index <= 0xffff); + elements[i].stream_index |= i << 16; + } + qsort(elements, element_count, sizeof(elements[0]), signature_element_register_compare); + + for (i = 0, new_count = 0; i < element_count; i = j, elements[new_count++] = *e) + { + e = &elements[i]; + j = i + 1; + + if (e->register_index == ~0u) + continue; + + /* Do not merge if the register index will be relative-addressed. */ + if (range_map_get_register_count(range_map, e->register_index, e->mask) > 1) + continue; + + for (; j < element_count; ++j) + { + f = &elements[j]; + + /* Merge different components of the same register unless sysvals are different, + * or it will be relative-addressed. */ + if (f->register_index != e->register_index || f->sysval_semantic != e->sysval_semantic + || range_map_get_register_count(range_map, f->register_index, f->mask) > 1) + break; + + TRACE("Merging %s, reg %u, mask %#x, sysval %#x with %s, mask %#x, sysval %#x.\n", e->semantic_name, + e->register_index, e->mask, e->sysval_semantic, f->semantic_name, f->mask, f->sysval_semantic); + assert(!(e->mask & f->mask)); + + e->mask |= f->mask; + e->used_mask |= f->used_mask; + e->semantic_index = min(e->semantic_index, f->semantic_index); + } + } + element_count = new_count; + /* Signature 's' is a copy of the original signature struct, so we can replace + * the 'elements' pointer without freeing it. */ + s->elements = elements; + s->element_count = element_count; + + if (is_patch_constant) + shader_signature_map_patch_constant_index_ranges(s, range_map); + + for (i = 0, new_count = 0; i < element_count; i += register_count, elements[new_count++] = *e) + { + e = &elements[i]; + register_count = 1; + + if (e->register_index >= MAX_REG_OUTPUT) + continue; + + register_count = range_map_get_register_count(range_map, e->register_index, e->mask); + if (register_count == UINT8_MAX) + { + /* Indices which land inside a range should have been skipped in a valid and correctly sorted signature. */ + FIXME("Invalid register index %u.\n", e->register_index); + continue; + } + register_count += !register_count; + + if (register_count > 1) + { + TRACE("Merging %s, base reg %u, count %u.\n", e->semantic_name, e->register_index, register_count); + e->register_count = register_count; + } + } + element_count = new_count; + + /* Restoring the original order is required for sensible trace output. */ + qsort(elements, element_count, sizeof(elements[0]), signature_element_index_compare); + for (i = 0; i < element_count; ++i) + elements[i].stream_index &= 0xffff; + + s->element_count = element_count; + + return true; +} + +static bool sysval_semantic_is_tess_factor(enum vkd3d_shader_sysval_semantic sysval_semantic) +{ + return sysval_semantic >= VKD3D_SHADER_SV_TESS_FACTOR_QUADEDGE + && sysval_semantic <= VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN; +} + +static void shader_register_init(struct vkd3d_shader_register *reg, + enum vkd3d_shader_register_type reg_type, enum vkd3d_data_type data_type) +{ + reg->type = reg_type; + reg->precision = VKD3D_SHADER_REGISTER_PRECISION_DEFAULT; + reg->non_uniform = false; + reg->data_type = data_type; + reg->idx[0].offset = ~0u; + reg->idx[0].rel_addr = NULL; + reg->idx[1].offset = ~0u; + reg->idx[1].rel_addr = NULL; + reg->idx[2].offset = ~0u; + reg->idx[2].rel_addr = NULL; + reg->immconst_type = VKD3D_IMMCONST_SCALAR; +} + +static struct vkd3d_shader_src_param *shader_normaliser_create_outpointid_param(struct vkd3d_shader_normaliser *normaliser) +{ + struct vkd3d_shader_src_param *rel_addr; + + if (!(rel_addr = shader_src_param_allocator_get(&normaliser->instructions.src_params, 1))) + return NULL; + + shader_register_init(&rel_addr->reg, VKD3DSPR_OUTPOINTID, VKD3D_DATA_UINT); + rel_addr->swizzle = 0; + rel_addr->modifiers = 0; + + return rel_addr; +} + +static unsigned int shader_register_normalise_arrayed_addressing(struct vkd3d_shader_register *reg, + unsigned int id_idx, unsigned int register_index) +{ + assert(id_idx < ARRAY_SIZE(reg->idx) - 1); + + /* For a relative-addressed register index, move the id up a slot to separate it from the address, + * because rel_addr can be replaced with a constant offset in some cases. */ + if (reg->idx[id_idx].rel_addr) + { + reg->idx[id_idx + 1].rel_addr = NULL; + reg->idx[id_idx + 1].offset = reg->idx[id_idx].offset; + reg->idx[id_idx].offset = 0; + ++id_idx; + } + /* Otherwise we have no address for the arrayed register, so insert one. This happens e.g. where + * tessellation level registers are merged into an array because they're an array in SPIR-V. */ + else + { + ++id_idx; + memmove(®->idx[1], ®->idx[0], id_idx * sizeof(reg->idx[0])); + reg->idx[0].rel_addr = NULL; + reg->idx[0].offset = reg->idx[id_idx].offset - register_index; + } + + return id_idx; +} + +static int shader_dst_param_io_normalise(struct vkd3d_shader_dst_param *dst_param, bool is_io_dcl, + struct vkd3d_shader_normaliser *normaliser, struct spirv_compiler *compiler) +{ + struct vkd3d_shader_register *reg = &dst_param->reg; + const struct vkd3d_shader_signature *signature; + const struct vkd3d_shader_signature_element *e; + struct vkd3d_shader_dst_param **dcl_params; + unsigned int id_idx, element_idx; + + if ((reg->type == VKD3DSPR_OUTPUT && normaliser_is_in_fork_or_join_phase(normaliser)) + || reg->type == VKD3DSPR_PATCHCONST) + { + signature = &compiler->patch_constant_signature; + /* Convert patch constant outputs to the patch constant register type to avoid the need + * to convert compiler symbols when accessed as inputs in a later stage. */ + reg->type = VKD3DSPR_PATCHCONST; + dcl_params = normaliser->pc_dcl_params; + } + else if (reg->type == VKD3DSPR_OUTPUT || dst_param->reg.type == VKD3DSPR_COLOROUT) + { + signature = &compiler->output_signature; + dcl_params = normaliser->output_dcl_params; + } + else if (dst_param->reg.type == VKD3DSPR_INCONTROLPOINT || dst_param->reg.type == VKD3DSPR_INPUT) + { + signature = &compiler->input_signature; + dcl_params = normaliser->input_dcl_params; + } + else + { + return 1; + } + + id_idx = reg->idx[1].offset != ~0u ? 1 : 0; + + element_idx = shader_signature_find_element_for_reg(signature, reg->idx[id_idx].offset, dst_param->write_mask); + if (element_idx == ~0u) + { + FIXME("Failed to find signature element for register.\n"); + return -1; + } + e = &signature->elements[element_idx]; + + dst_param->write_mask >>= vkd3d_write_mask_get_component_idx(e->mask); + if (is_io_dcl) + { + if (element_idx >= ARRAY_SIZE(normaliser->input_dcl_params)) + { + FIXME("Unsupported signature index %u.\n", element_idx); + } + else if (dcl_params[element_idx]) + { + /* Merge split declarations into a single one. */ + dcl_params[element_idx]->write_mask |= dst_param->write_mask; + /* Turn this into a nop. */ + return 0; + } + else + { + dcl_params[element_idx] = dst_param; + } + } + + if (normaliser_is_in_control_point_phase(normaliser) && reg->type == VKD3DSPR_OUTPUT) + { + assert(!id_idx); + reg->idx[1] = reg->idx[0]; + if (is_io_dcl) + { + /* Emit an array size for the control points for consistency with inputs. */ + reg->idx[0].offset = normaliser->output_control_point_count; + } + else + { + /* The control point id param is implicit here. Avoid later complications by inserting it. */ + reg->idx[0].offset = 0; + reg->idx[0].rel_addr = normaliser->outpointid_param; + } + id_idx = 1; + } + + if ((e->register_count > 1 || sysval_semantic_is_tess_factor(e->sysval_semantic))) + { + if (is_io_dcl) + { + assert(!id_idx || (id_idx == 1 && normaliser_is_in_control_point_phase(normaliser))); + /* In the control point phase this contains the control point count. + * Move it up to the next slot. */ + reg->idx[id_idx].offset = reg->idx[0].offset; + reg->idx[0].offset = e->register_count; + ++id_idx; + } + else + { + id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index); + } + } + + /* Replace the register index with the signature element index */ + reg->idx[id_idx].offset = element_idx; + + return 1; +} + +static int shader_src_param_io_normalise(struct vkd3d_shader_src_param *src_param, struct spirv_compiler *compiler) +{ + struct vkd3d_shader_register *reg = &src_param->reg; + unsigned int i, id_idx, element_idx, component_idx; + const struct vkd3d_shader_signature *signature; + const struct vkd3d_shader_signature_element *e; + + if (src_param->reg.type == VKD3DSPR_PATCHCONST) + { + signature = &compiler->patch_constant_signature; + } + else if (src_param->reg.type == VKD3DSPR_INCONTROLPOINT || src_param->reg.type == VKD3DSPR_INPUT) + { + signature = &compiler->input_signature; + } + else if (src_param->reg.type == VKD3DSPR_OUTPUT) + { + signature = &compiler->output_signature; + } + else + { + return 1; + } + + id_idx = reg->idx[1].offset != ~0u ? 1 : 0; + + element_idx = shader_signature_find_element_for_reg(signature, reg->idx[id_idx].offset, + VKD3DSP_WRITEMASK_0 << vkd3d_swizzle_get_component(src_param->swizzle, 0)); + if (element_idx == ~0u) + { + FIXME("Failed to find signature element for register.\n"); + return -1; + } + + e = &signature->elements[element_idx]; + if ((e->register_count > 1 || sysval_semantic_is_tess_factor(e->sysval_semantic))) + id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index); + reg->idx[id_idx].offset = element_idx; + + if ((component_idx = vkd3d_write_mask_get_component_idx(e->mask))) + { + for (i = 0; i < VKD3D_VEC4_SIZE; ++i) + if (vkd3d_swizzle_get_component(src_param->swizzle, i)) + src_param->swizzle -= component_idx << VKD3D_SHADER_SWIZZLE_SHIFT(i); + } + + return 1; +} + static void shader_instruction_init(struct vkd3d_shader_instruction *ins, enum vkd3d_shader_opcode handler_idx) { memset(ins, 0, sizeof(*ins)); ins->handler_idx = handler_idx; }
+static inline void shader_dst_param_io_init(struct vkd3d_shader_dst_param *param, + const struct vkd3d_shader_signature_element *e, enum vkd3d_shader_register_type reg_type) +{ + param->write_mask = e->mask; + param->modifiers = 0; + param->shift = 0; + shader_register_init(¶m->reg, reg_type, vkd3d_data_type_from_component_type(e->component_type)); +} + +static enum vkd3d_result shader_normaliser_emit_hs_input(struct vkd3d_shader_normaliser *normaliser, + const struct vkd3d_shader_signature *s) +{ + const struct vkd3d_shader_signature_element *e; + struct vkd3d_shader_instruction *ins; + struct vkd3d_shader_dst_param *param; + unsigned int i, j; + + if (!shader_normaliser_new_instructions(normaliser, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &normaliser->instructions.elements[normaliser->instructions.count++]; + shader_instruction_init(ins, VKD3DSIH_HS_CONTROL_POINT_PHASE); + ins->flags = 1; + + for (i = 0; i < s->element_count; ++i) + { + e = &s->elements[i]; + if (!e->used_mask) + continue; + + if (!shader_normaliser_new_instructions(normaliser, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + ins = &normaliser->instructions.elements[normaliser->instructions.count++]; + + if (e->sysval_semantic != VKD3D_SHADER_SV_NONE) + { + shader_instruction_init(ins, VKD3DSIH_DCL_INPUT_SIV); + param = &ins->declaration.register_semantic.reg; + ins->declaration.register_semantic.sysval_semantic = vkd3d_siv_from_sysval(e->sysval_semantic); + } + else + { + shader_instruction_init(ins, VKD3DSIH_DCL_INPUT); + param = &ins->declaration.dst; + } + + shader_dst_param_io_init(param, e, VKD3DSPR_INPUT); + j = 0; + if (e->register_count > 1) + param->reg.idx[j++].offset = e->register_count; + param->reg.idx[j++].offset = normaliser->input_control_point_count; + param->reg.idx[j].offset = i; + } + + return VKD3D_OK; +} + +static enum vkd3d_result shader_instruction_normalise_io_params(struct vkd3d_shader_instruction *ins, + struct spirv_compiler *compiler, struct vkd3d_shader_normaliser *normaliser) +{ + unsigned int i; + int result = 1; + + switch (ins->handler_idx) + { + case VKD3DSIH_DCL_INPUT: + case VKD3DSIH_DCL_INPUT_PS: + case VKD3DSIH_DCL_OUTPUT: + result = shader_dst_param_io_normalise(&ins->declaration.dst, true, normaliser, compiler); + break; + case VKD3DSIH_DCL_INPUT_SGV: + case VKD3DSIH_DCL_INPUT_SIV: + case VKD3DSIH_DCL_INPUT_PS_SGV: + case VKD3DSIH_DCL_INPUT_PS_SIV: + case VKD3DSIH_DCL_OUTPUT_SIV: + result = shader_dst_param_io_normalise(&ins->declaration.register_semantic.reg, true, + normaliser, compiler); + break; + case VKD3DSIH_HS_CONTROL_POINT_PHASE: + case VKD3DSIH_HS_FORK_PHASE: + case VKD3DSIH_HS_JOIN_PHASE: + normaliser->phase = ins->handler_idx; + memset(normaliser->input_dcl_params, 0, sizeof(normaliser->input_dcl_params)); + memset(normaliser->output_dcl_params, 0, sizeof(normaliser->output_dcl_params)); + memset(normaliser->pc_dcl_params, 0, sizeof(normaliser->pc_dcl_params)); + break; + default: + if (is_dcl_instruction(ins->handler_idx)) + break; + for (i = 0; i < ins->dst_count; ++i) + result |= shader_dst_param_io_normalise((struct vkd3d_shader_dst_param *)&ins->dst[i], false, + normaliser, compiler); + for (i = 0; i < ins->src_count; ++i) + result |= shader_src_param_io_normalise((struct vkd3d_shader_src_param *)&ins->src[i], compiler); + break; + } + + if (!result) + shader_instruction_init(ins, VKD3DSIH_NOP); + + return result >= 0 ? VKD3D_OK : VKD3D_ERROR_INVALID_SHADER; +} + static enum vkd3d_result shader_normaliser_normalise(struct vkd3d_shader_normaliser *normaliser, const struct vkd3d_shader_instruction_array *instructions, struct spirv_compiler *compiler) { const struct vkd3d_shader_instruction *ins; + unsigned int i, j, instruction_count; enum vkd3d_result result = VKD3D_OK; - unsigned int i, instruction_count;
memset(normaliser, 0, sizeof(*normaliser)); normaliser->phase = VKD3DSIH_INVALID; @@ -10080,6 +10353,12 @@ static enum vkd3d_result shader_normaliser_normalise(struct vkd3d_shader_normali ERR("Failed to allocate instructions.\n"); return VKD3D_ERROR_OUT_OF_MEMORY; } + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL + && !(normaliser->outpointid_param = shader_normaliser_create_outpointid_param(normaliser))) + { + ERR("Failed to allocate src param.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + }
for (i = 0; i < instructions->count; ++i) { @@ -10088,13 +10367,40 @@ static enum vkd3d_result shader_normaliser_normalise(struct vkd3d_shader_normali
ins = &instructions->elements[i];
+ if (shader_instruction_is_fork_or_join_phase(ins) + && !normaliser->has_control_point_phase && !normaliser_is_in_fork_or_join_phase(normaliser) + && (result = shader_normaliser_emit_hs_input(normaliser, &compiler->input_signature)) < 0) + { + return result; + } + if (!shader_instruction_array_clone_instruction(&normaliser->instructions, normaliser->instructions.count, ins)) return VKD3D_ERROR_OUT_OF_MEMORY;
instruction_count = 1; - if ((result = shader_normaliser_eliminate_phase_instance_id(normaliser, &instruction_count)) < 0) + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL && (result = shader_normaliser_eliminate_phase_instance_id( + normaliser, &instruction_count)) < 0) return result;
+ switch (ins->handler_idx) + { + case VKD3DSIH_DCL_INPUT_CONTROL_POINT_COUNT: + normaliser->input_control_point_count = ins->declaration.count; + break; + case VKD3DSIH_DCL_OUTPUT_CONTROL_POINT_COUNT: + normaliser->output_control_point_count = ins->declaration.count; + break; + case VKD3DSIH_DCL_INDEX_RANGE: + shader_normaliser_add_index_range(normaliser, ins, compiler); + instruction_count = 0; + break; + case VKD3DSIH_HS_CONTROL_POINT_PHASE: + normaliser->has_control_point_phase = true; + break; + default: + break; + } + normaliser->instructions.count += instruction_count; }
@@ -10108,6 +10414,30 @@ static enum vkd3d_result shader_normaliser_normalise(struct vkd3d_shader_normali shader_instruction_init(&normaliser->instructions.elements[normaliser->instructions.count++], VKD3DSIH_RET); }
+ if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL && !normaliser->has_control_point_phase) + { + /* Inputs and outputs must match for the default phase, so merge ranges must match too. */ + for (i = 0; i < MAX_REG_OUTPUT; ++i) + for (j = 0; j < VKD3D_VEC4_SIZE; ++j) + if (!normaliser->input_range_map[i][j] && normaliser->output_range_map[i][j]) + normaliser->input_range_map[i][j] = normaliser->output_range_map[i][j]; + else if (normaliser->input_range_map[i][j] && !normaliser->output_range_map[i][j]) + normaliser->output_range_map[i][j] = normaliser->input_range_map[i][j]; + else if (normaliser->input_range_map[i][j] != normaliser->output_range_map[i][j]) + FIXME("Input and output range maps collide.\n"); + } + + if (!shader_signature_merge(&compiler->input_signature, normaliser->input_range_map, false) + || !shader_signature_merge(&compiler->output_signature, normaliser->output_range_map, false) + || !shader_signature_merge(&compiler->patch_constant_signature, normaliser->pc_range_map, true)) + { + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + normaliser->phase = VKD3DSIH_INVALID; + for (i = 0; i < normaliser->instructions.count && result >= 0; ++i) + result = shader_instruction_normalise_io_params(&normaliser->instructions.elements[i], compiler, normaliser); + return result; }
@@ -10123,18 +10453,17 @@ int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, enum vkd3d_result result = VKD3D_OK; unsigned int i;
- if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) - { - if ((result = shader_normaliser_normalise(&normaliser, instructions, compiler)) < 0) - return result; - instructions = &normaliser.instructions; - } + if ((result = shader_normaliser_normalise(&normaliser, instructions, compiler)) < 0) + return result; + instructions = &normaliser.instructions; + + if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) + spirv_compiler_emit_shader_signature_outputs(compiler);
for (i = 0; i < instructions->count && result >= 0; ++i) result = spirv_compiler_handle_instruction(compiler, &instructions->elements[i]);
- if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) - shader_normaliser_destroy(&normaliser); + shader_normaliser_destroy(&normaliser);
if (result < 0) return result; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 2108f6e8..1146b34f 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -924,6 +924,11 @@ static inline bool vkd3d_shader_register_is_output(const struct vkd3d_shader_reg return reg->type == VKD3DSPR_OUTPUT || reg->type == VKD3DSPR_COLOROUT; }
+static inline bool vkd3d_shader_register_is_patch_constant(const struct vkd3d_shader_register *reg) +{ + return reg->type == VKD3DSPR_PATCHCONST; +} + static inline bool vkd3d_shader_register_is_phase_instance_id(const struct vkd3d_shader_register *reg) { return reg->type == VKD3DSPR_FORKINSTID || reg->type == VKD3DSPR_JOININSTID; diff --git a/tests/d3d12.c b/tests/d3d12.c index fdd349dc..308f44ac 100644 --- a/tests/d3d12.c +++ b/tests/d3d12.c @@ -36255,7 +36255,6 @@ static void test_vs_ps_relative_addressing(void)
transition_resource_state(command_list, context.render_target, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE); - todo check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
ID3D12Resource_Release(vb);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/spirv.c | 9 +++++ libs/vkd3d-shader/trace.c | 45 +++++++++++++++++++++--- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index ea48dab5..5704678b 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -10457,6 +10457,15 @@ int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, return result; instructions = &normaliser.instructions;
+ if (TRACE_ON()) + { + struct vkd3d_shader_parser tmp_parser; + tmp_parser.shader_version = parser->shader_version; + tmp_parser.instructions = *instructions; + tmp_parser.is_normalised = true; + vkd3d_shader_trace(&tmp_parser); + } + if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) spirv_compiler_emit_shader_signature_outputs(compiler);
diff --git a/libs/vkd3d-shader/trace.c b/libs/vkd3d-shader/trace.c index 8e2cac16..0323c016 100644 --- a/libs/vkd3d-shader/trace.c +++ b/libs/vkd3d-shader/trace.c @@ -355,6 +355,8 @@ struct vkd3d_d3d_asm_compiler struct vkd3d_string_buffer buffer; struct vkd3d_shader_version shader_version; struct vkd3d_d3d_asm_colours colours; + bool is_normalised; + bool is_in_fork_or_join_phase; };
static int shader_ver_ge(const struct vkd3d_shader_version *v, int major, int minor) @@ -833,12 +835,23 @@ static void shader_print_subscript_range(struct vkd3d_d3d_asm_compiler *compiler vkd3d_string_buffer_printf(&compiler->buffer, "*]"); }
+static int shader_register_input_output_id_idx(const struct vkd3d_shader_register *reg) +{ + int i; + for (i = 2; i >= 0; --i) + if (reg->idx[i].offset != ~0u) + return i; + assert(false); + return 0; +} + static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const struct vkd3d_shader_register *reg, bool is_declaration) { struct vkd3d_string_buffer *buffer = &compiler->buffer; unsigned int offset = reg->idx[0].offset; bool is_descriptor = false; + int io_id_idx = -1;
static const char * const rastout_reg_names[] = {"oPos", "oFog", "oPts"}; static const char * const misctype_reg_names[] = {"vPos", "vFace"}; @@ -852,6 +865,7 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const
case VKD3DSPR_INPUT: shader_addline(buffer, "v"); + io_id_idx = shader_register_input_output_id_idx(reg); break;
case VKD3DSPR_CONST: @@ -900,6 +914,7 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const shader_addline(buffer, "o"); else shader_addline(buffer, "oT"); + io_id_idx = shader_register_input_output_id_idx(reg); break;
case VKD3DSPR_CONSTINT: @@ -992,14 +1007,17 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const
case VKD3DSPR_INCONTROLPOINT: shader_addline(buffer, "vicp"); + io_id_idx = shader_register_input_output_id_idx(reg); break;
case VKD3DSPR_OUTCONTROLPOINT: shader_addline(buffer, "vocp"); + io_id_idx = shader_register_input_output_id_idx(reg); break;
case VKD3DSPR_PATCHCONST: - shader_addline(buffer, "vpc"); + shader_addline(buffer, compiler->is_in_fork_or_join_phase ? "opc" : "vpc"); + io_id_idx = shader_register_input_output_id_idx(reg); break;
case VKD3DSPR_TESSCOORD: @@ -1153,18 +1171,23 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const if (offset != ~0u) { bool is_sm_5_1 = shader_ver_ge(&compiler->shader_version, 5, 1); + unsigned int id_idx; + + id_idx = (io_id_idx >= 0 && compiler->is_normalised) ? io_id_idx : 0;
- if (reg->idx[0].rel_addr || reg->type == VKD3DSPR_IMMCONSTBUFFER + if (reg->idx[id_idx].rel_addr || reg->type == VKD3DSPR_IMMCONSTBUFFER || reg->type == VKD3DSPR_INCONTROLPOINT || (reg->type == VKD3DSPR_INPUT && (compiler->shader_version.type == VKD3D_SHADER_TYPE_GEOMETRY || compiler->shader_version.type == VKD3D_SHADER_TYPE_HULL))) { + if (!reg->idx[id_idx].rel_addr) + id_idx = 0; vkd3d_string_buffer_printf(buffer, "%s", compiler->colours.reset); - shader_print_subscript(compiler, offset, reg->idx[0].rel_addr); + shader_print_subscript(compiler, reg->idx[id_idx].offset, reg->idx[id_idx].rel_addr); } else { - vkd3d_string_buffer_printf(buffer, "%u%s", offset, compiler->colours.reset); + vkd3d_string_buffer_printf(buffer, "%u%s", reg->idx[id_idx].offset, compiler->colours.reset); }
/* For sm 5.1 descriptor declarations we need to print the register range instead of @@ -1173,6 +1196,13 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const { shader_print_subscript_range(compiler, reg->idx[1].offset, reg->idx[2].offset); } + else if (compiler->is_normalised) + { + int i; + for (i = 0; i <= io_id_idx; ++i) + if (i != id_idx) + shader_print_subscript(compiler, reg->idx[i].offset, reg->idx[i].rel_addr); + } else { /* For descriptors in sm < 5.1 we move the reg->idx values up one slot @@ -1778,6 +1808,11 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, shader_dump_register_space(compiler, ins->declaration.structured_resource.resource.range.space); break;
+ case VKD3DSIH_HS_FORK_PHASE: + case VKD3DSIH_HS_JOIN_PHASE: + compiler->is_in_fork_or_join_phase = true; + break; + case VKD3DSIH_DEF: vkd3d_string_buffer_printf(buffer, " %sc%u%s", compiler->colours.reg, shader_get_float_offset(ins->dst[0].reg.type, ins->dst[0].reg.idx[0].offset), @@ -1920,6 +1955,8 @@ enum vkd3d_result vkd3d_dxbc_binary_to_text(struct vkd3d_shader_parser *parser, shader_version->minor, compiler.colours.reset);
indent = 0; + compiler.is_normalised = parser->is_normalised; + compiler.is_in_fork_or_join_phase = false; for (i = 0; i < parser->instructions.count; ++i) { struct vkd3d_shader_instruction *ins = &parser->instructions.elements[i]; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 1146b34f..df5161b5 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1002,6 +1002,7 @@ struct vkd3d_shader_parser const struct vkd3d_shader_parser_ops *ops; struct vkd3d_shader_instruction_array instructions; size_t instruction_idx; + bool is_normalised; };
struct vkd3d_shader_parser_ops
I didn't review this yet, but this change did catch my eye:
diff --git a/include/vkd3d_shader.h b/include/vkd3d_shader.h index 86ffb9d5..3b106e68 100644 --- a/include/vkd3d_shader.h +++ b/include/vkd3d_shader.h @@ -1444,6 +1444,11 @@ struct vkd3d_shader_signature_element unsigned int used_mask; /** Minimum interpolation precision. */ enum vkd3d_shader_minimum_precision min_precision; + /** + * Register count. A value of 0 or 1 indicates \a register_index is the + * only register. Values greater than 4 are invalid. \since 1.7 + */ + unsigned int register_count; }; /**
You can't do that; the vkd3d_shader_signature_element structure is part of the public API.
Without having looked much at the contents, I notice patch 1/4 and 3/4 are quite large; if those could be split that would be much appreciated.
I haven't looked at really any of these patches (as Henri mentioned, they're awfully large), but it seems surprising that we're doing this normalization in spirv.c, and not in the dxbc/sm4 frontend.
If we don't add a register count to vkd3d_shader_signature_element then API users can't specify SM 6 signatures with it, because they contain a register count ('row count' in the docs but it amounts to the same).
Patch 1 contains only what's required to resolve multiple fork/join phases into one. I see no intermediate changes which can be made.
I can probably split off a few small changes from patch 3: merging split I/O registers, separating the relative address from the register index for register relative indexing, inserting the control point count or id in reg.idx where it's missing, and creating a partial default control point phase in the IR. For the remainder, the problem is once the signatures are normalised then all the changes are necessary for it to work.
It may be feasible to normalise only input signatures, then add outputs later, or vice-versa, bearing in mind sometimes the patch constants are inputs.
The location for the normalisation is debatable. Henri and I agreed spirv.c is probably the best place because these changes are intended to make emitting SPIR-V easier. It can also be argued that since the SM 6 parser naturally emits in this form, except for one or two fixups, it's not all that related to SPIR-V.
In patch 1 I can probably split the removal of the phase array from the other changes.
If we don't add a register count to vkd3d_shader_signature_element then API users can't specify SM 6 signatures with it, because they contain a register count ('row count' in the docs but it amounts to the same).
Sure, it probably makes sense for there to be some way to return complete information for sm6 input signatures (though note that e.g. D3D12_SIGNATURE_PARAMETER_DESC doesn't appear to include this either); we just can't do it by changing the existing vkd3d_shader_signature_element structure because that breaks the ABI. Perhaps most notably because the vkd3d_shader_signature structure contains an array of those.
The location for the normalisation is debatable. Henri and I agreed spirv.c is probably the best place because these changes are intended to make emitting SPIR-V easier. It can also be argued that since the SM 6 parser naturally emits in this form, except for one or two fixups, it's not all that related to SPIR-V.
Right, we may change our mind about this at some point in the future. For the moment though, one of the main considerations is that we'd like to preserve the original structure long enough for the d3d-asm target to be able to output it.
The location for the normalisation is debatable. Henri and I agreed spirv.c is probably the best place because these changes are intended to make emitting SPIR-V easier. It can also be argued that since the SM 6 parser naturally emits in this form, except for one or two fixups, it's not all that related to SPIR-V.
Right, we may change our mind about this at some point in the future. For the moment though, one of the main considerations is that we'd like to preserve the original structure long enough for the d3d-asm target to be able to output it.
I feel like trying to accommodate the assembler (and disassembler) in vkd3d_shader_instruction is going to be a losing proposition at some point, but in this case it at least doesn't seem right that it's in spirv.c; perhaps it could live in some "neutral" source file?
Looks like it could be placed in its own source file pretty neatly.
I'll remind everyone, given the magnitude of the changes in the largest patch, we still have the alternative of running SM 6 I/O declarations through separate functions. They would be about the size of these patched spirv_compiler_emit_input() and spirv_compiler_emit_output() functions. The downside is it doesn't eliminate most private variables or fix the issue with register relative addressing. Those could still be done another way though. I would still keep the phase elimination.