From 0c0b6c45dabb10bbc8ef60037da3ba19a98cadd7 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Sat, 29 Nov 2025 17:43:56 +1100 Subject: [PATCH] Updated vkd3d-latest patchset --- ...-e9d08df0108e1a6b7dd67b37f3ed2e35f98.patch | 2 +- ...-54aa285b307a8b3b7c914487290a52efc6b.patch | 2183 +++++++++++++++++ ...-fdfb74b20b08144e144299bc0b7b20b9542.patch | 120 + 3 files changed, 2304 insertions(+), 1 deletion(-) create mode 100644 patches/vkd3d-latest/0002-Updated-vkd3d-to-54aa285b307a8b3b7c914487290a52efc6b.patch create mode 100644 patches/vkd3d-latest/0003-Updated-vkd3d-to-fdfb74b20b08144e144299bc0b7b20b9542.patch diff --git a/patches/vkd3d-latest/0001-Updated-vkd3d-to-e9d08df0108e1a6b7dd67b37f3ed2e35f98.patch b/patches/vkd3d-latest/0001-Updated-vkd3d-to-e9d08df0108e1a6b7dd67b37f3ed2e35f98.patch index 01f5f354..5d08c608 100644 --- a/patches/vkd3d-latest/0001-Updated-vkd3d-to-e9d08df0108e1a6b7dd67b37f3ed2e35f98.patch +++ b/patches/vkd3d-latest/0001-Updated-vkd3d-to-e9d08df0108e1a6b7dd67b37f3ed2e35f98.patch @@ -1,4 +1,4 @@ -From c5eac13627c57671945e43319ed0b5c7a299c100 Mon Sep 17 00:00:00 2001 +From 0be0b9877acf14f602d6cde0becea8ca631e884e Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Tue, 25 Nov 2025 12:56:39 +1100 Subject: [PATCH] Updated vkd3d to e9d08df0108e1a6b7dd67b37f3ed2e35f98100d1. diff --git a/patches/vkd3d-latest/0002-Updated-vkd3d-to-54aa285b307a8b3b7c914487290a52efc6b.patch b/patches/vkd3d-latest/0002-Updated-vkd3d-to-54aa285b307a8b3b7c914487290a52efc6b.patch new file mode 100644 index 00000000..a68e3fe5 --- /dev/null +++ b/patches/vkd3d-latest/0002-Updated-vkd3d-to-54aa285b307a8b3b7c914487290a52efc6b.patch @@ -0,0 +1,2183 @@ +From 2dc45123985702b6de8aadcae18e48b62f49999a Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Thu, 27 Nov 2025 13:40:15 +1100 +Subject: [PATCH] Updated vkd3d to 54aa285b307a8b3b7c914487290a52efc6b4d920. + +--- + libs/vkd3d/libs/vkd3d-shader/d3dbc.c | 2 +- + libs/vkd3d/libs/vkd3d-shader/dxbc.c | 13 +- + libs/vkd3d/libs/vkd3d-shader/glsl.c | 4 +- + libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c | 172 +++- + libs/vkd3d/libs/vkd3d-shader/ir.c | 804 +++++++++++++++++- + libs/vkd3d/libs/vkd3d-shader/msl.c | 4 +- + libs/vkd3d/libs/vkd3d-shader/spirv.c | 210 +---- + libs/vkd3d/libs/vkd3d-shader/tpf.c | 15 +- + .../libs/vkd3d-shader/vkd3d_shader_main.c | 50 +- + .../libs/vkd3d-shader/vkd3d_shader_private.h | 63 +- + libs/vkd3d/libs/vkd3d/device.c | 17 + + libs/vkd3d/libs/vkd3d/resource.c | 38 +- + libs/vkd3d/libs/vkd3d/vkd3d_private.h | 1 + + 13 files changed, 1084 insertions(+), 309 deletions(-) + +diff --git a/libs/vkd3d/libs/vkd3d-shader/d3dbc.c b/libs/vkd3d/libs/vkd3d-shader/d3dbc.c +index 87a7d48acca..65c469e9a71 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/d3dbc.c ++++ b/libs/vkd3d/libs/vkd3d-shader/d3dbc.c +@@ -1595,7 +1595,7 @@ int d3dbc_parse(const struct vkd3d_shader_compile_info *compile_info, uint64_t c + } + } + +- program->has_descriptor_info = true; ++ program->normalisation_flags.has_descriptor_info = true; + + if (TRACE_ON()) + vsir_program_trace(program); +diff --git a/libs/vkd3d/libs/vkd3d-shader/dxbc.c b/libs/vkd3d/libs/vkd3d-shader/dxbc.c +index 45a45c3ad4a..f1533fbcd54 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/dxbc.c ++++ b/libs/vkd3d/libs/vkd3d-shader/dxbc.c +@@ -340,11 +340,7 @@ int vkd3d_shader_parse_dxbc(const struct vkd3d_shader_code *dxbc, + ret = parse_dxbc(dxbc, &message_context, NULL, flags, desc); + + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages) && ret >= 0) +- { +- vkd3d_shader_free_dxbc(desc); +- ret = VKD3D_ERROR_OUT_OF_MEMORY; +- } ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + + if (ret < 0) +@@ -1106,9 +1102,7 @@ int vkd3d_shader_parse_root_signature(const struct vkd3d_shader_code *dxbc, + + ret = for_each_dxbc_section(dxbc, &message_context, NULL, rts0_handler, root_signature); + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; +- ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + if (ret < 0) + vkd3d_shader_free_root_signature(root_signature); +@@ -1558,8 +1552,7 @@ int vkd3d_shader_serialize_root_signature(const struct vkd3d_shader_versioned_ro + + done: + vkd3d_shader_message_context_trace_messages(&context.message_context); +- if (!vkd3d_shader_message_context_copy_messages(&context.message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; ++ vkd3d_shader_string_from_message_context(messages, &context.message_context); + vkd3d_shader_message_context_cleanup(&context.message_context); + return ret; + } +diff --git a/libs/vkd3d/libs/vkd3d-shader/glsl.c b/libs/vkd3d/libs/vkd3d-shader/glsl.c +index 4d7505d8740..2e41a8609e4 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/glsl.c ++++ b/libs/vkd3d/libs/vkd3d-shader/glsl.c +@@ -2481,8 +2481,8 @@ int glsl_compile(struct vsir_program *program, uint64_t config_flags, + return ret; + + VKD3D_ASSERT(program->normalisation_level == VSIR_NORMALISED_SM6); +- VKD3D_ASSERT(program->has_descriptor_info); +- VKD3D_ASSERT(program->has_no_modifiers); ++ VKD3D_ASSERT(program->normalisation_flags.has_descriptor_info); ++ VKD3D_ASSERT(program->normalisation_flags.has_no_modifiers); + + vkd3d_glsl_generator_init(&generator, program, compile_info, + combined_sampler_info, message_context); +diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c +index f1368b151aa..6add89969d9 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c ++++ b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c +@@ -1413,24 +1413,49 @@ static struct hlsl_ir_node *lower_matrix_swizzles(struct hlsl_ctx *ctx, + return hlsl_block_add_simple_load(ctx, block, var, &instr->loc); + } + +-/* hlsl_ir_index nodes are a parse-time construct used to represent array indexing and struct +- * record access before knowing if they will be used in the lhs of an assignment --in which case +- * they are lowered into a deref-- or as the load of an element within a larger value. +- * For the latter case, this pass takes care of lowering hlsl_ir_indexes into individual +- * hlsl_ir_loads, or individual hlsl_ir_resource_loads, in case the indexing is a +- * resource access. */ +-static struct hlsl_ir_node *lower_index_loads(struct hlsl_ctx *ctx, +- struct hlsl_ir_node *instr, struct hlsl_block *block) ++/* Usually when INDEX nodes are constructed, it's a direct variable load ++ * followed by the INDEX. As described below in lower_index_load(), we know in ++ * that case that the variable in question is unmodified and we can convert the ++ * INDEX to a LOAD of the same variable instead of copying it to a temp. ++ * This function is an unsophisticated heuristic meant to detect this case. ++ * ++ * For various reasons there may be CONSTANT or EXPR instructions between the ++ * two, so we have to search until we find the source node. */ ++static bool is_indexed_value_known_unmodified(const struct hlsl_block *block, const struct hlsl_ir_index *index) ++{ ++ const struct list *entry = &index->node.entry; ++ ++ while ((entry = list_prev(&block->instrs, entry))) ++ { ++ const struct hlsl_ir_node *instr = LIST_ENTRY(entry, struct hlsl_ir_node, entry); ++ ++ if (instr == index->val.node) ++ return true; ++ ++ switch (instr->type) ++ { ++ case HLSL_IR_CONSTANT: ++ case HLSL_IR_EXPR: ++ break; ++ ++ default: ++ return false; ++ } ++ } ++ ++ return false; ++} ++ ++static struct hlsl_ir_node *lower_index_load(struct hlsl_ctx *ctx, struct hlsl_ir_index *index, ++ struct hlsl_block *block, struct hlsl_block *containing_block) + { ++ struct hlsl_ir_node *instr = &index->node; ++ const struct hlsl_deref *deref; + struct hlsl_deref var_deref; +- struct hlsl_ir_index *index; + struct hlsl_ir_load *load; + struct hlsl_ir_node *val; + struct hlsl_ir_var *var; + +- if (instr->type != HLSL_IR_INDEX) +- return NULL; +- index = hlsl_ir_index(instr); + val = index->val.node; + + if (hlsl_index_is_resource_access(index)) +@@ -1519,11 +1544,46 @@ static struct hlsl_ir_node *lower_index_loads(struct hlsl_ctx *ctx, + } + } + +- if (!(var = hlsl_new_synthetic_var(ctx, "index-val", val->data_type, &instr->loc))) +- return NULL; +- hlsl_init_simple_deref_from_var(&var_deref, var); ++ /* Indexed values don't have to be variable loads, but a LOAD must be of a ++ * variable, so we may need to copy the indexed value to a synthetic ++ * variable first. ++ * Even if an INDEX is of a variable load, due to the structure of our IR, ++ * it's legal for that variable to have been modified between the LOAD and ++ * the INDEX. For example, we can have a sequence like: ++ * ++ * 2: x ++ * 3: x = 1 ++ * 4: @2[...] ++ * ++ * Because the defined semantics of the IR are essentially "pass by value", ++ * we can't just convert @4 into a LOAD of x. We have to copy it into a ++ * synthetic temp first. ++ * ++ * This situation generally doesn't actually happen with the IR that comes ++ * from parsing, but it can happen in certain cases related to function ++ * calls. ++ * ++ * Always creating an extra copy is fine in theory, since copy propagation ++ * will later undo it. Some of these variables can be extremely large, ++ * however, such that we can observe a noticeable speed improvement by ++ * avoiding the copy in the first place. */ + +- hlsl_block_add_simple_store(ctx, block, var, val); ++ if (val->type == HLSL_IR_LOAD && is_indexed_value_known_unmodified(containing_block, index)) ++ { ++ /* Note that in a chain of indices only the first will be a LOAD. ++ * However, because we convert from top to bottom, and replace as we go, ++ * we should end up catching every index in a chain this way. */ ++ deref = &hlsl_ir_load(val)->src; ++ } ++ else ++ { ++ if (!(var = hlsl_new_synthetic_var(ctx, "index-val", val->data_type, &instr->loc))) ++ return NULL; ++ hlsl_init_simple_deref_from_var(&var_deref, var); ++ deref = &var_deref; ++ ++ hlsl_block_add_simple_store(ctx, block, var, val); ++ } + + if (hlsl_index_is_noncontiguous(index)) + { +@@ -1543,7 +1603,7 @@ static struct hlsl_ir_node *lower_index_loads(struct hlsl_ctx *ctx, + + c = hlsl_block_add_uint_constant(ctx, block, i, &instr->loc); + +- if (!(load = hlsl_new_load_index(ctx, &var_deref, c, &instr->loc))) ++ if (!(load = hlsl_new_load_index(ctx, deref, c, &instr->loc))) + return NULL; + hlsl_block_add_instr(block, &load->node); + +@@ -1557,7 +1617,67 @@ static struct hlsl_ir_node *lower_index_loads(struct hlsl_ctx *ctx, + return hlsl_block_add_simple_load(ctx, block, var, &instr->loc); + } + +- return hlsl_block_add_load_index(ctx, block, &var_deref, index->idx.node, &instr->loc); ++ return hlsl_block_add_load_index(ctx, block, deref, index->idx.node, &instr->loc); ++} ++ ++/* hlsl_ir_index nodes are a parse-time construct used to represent array ++ * indexing and struct record access before knowing if they will be used in the ++ * LHS of an assignment—in which case they are lowered into a deref—or as the ++ * load of an element within a larger value. ++ * For the latter case, this pass takes care of lowering hlsl_ir_indexes into ++ * individual hlsl_ir_load or hlsl_ir_resource_load. */ ++void hlsl_lower_index_loads(struct hlsl_ctx *ctx, struct hlsl_block *block) ++{ ++ struct hlsl_ir_node *instr, *next; ++ ++ LIST_FOR_EACH_ENTRY_SAFE(instr, next, &block->instrs, struct hlsl_ir_node, entry) ++ { ++ switch (instr->type) ++ { ++ case HLSL_IR_INDEX: ++ { ++ struct hlsl_ir_node *replacement; ++ struct hlsl_block new_block; ++ ++ hlsl_block_init(&new_block); ++ if ((replacement = lower_index_load(ctx, hlsl_ir_index(instr), &new_block, block))) ++ { ++ list_move_before(&instr->entry, &new_block.instrs); ++ hlsl_replace_node(instr, replacement); ++ } ++ else ++ { ++ hlsl_block_cleanup(&new_block); ++ } ++ break; ++ } ++ ++ case HLSL_IR_IF: ++ { ++ struct hlsl_ir_if *iff = hlsl_ir_if(instr); ++ hlsl_lower_index_loads(ctx, &iff->then_block); ++ hlsl_lower_index_loads(ctx, &iff->else_block); ++ break; ++ } ++ ++ case HLSL_IR_LOOP: ++ hlsl_lower_index_loads(ctx, &hlsl_ir_loop(instr)->body); ++ break; ++ ++ case HLSL_IR_SWITCH: ++ { ++ struct hlsl_ir_switch *s = hlsl_ir_switch(instr); ++ struct hlsl_ir_switch_case *c; ++ ++ LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) ++ hlsl_lower_index_loads(ctx, &c->body); ++ break; ++ } ++ ++ default: ++ break; ++ } ++ } + } + + /* Lower casts from vec1 to vecN to swizzles. */ +@@ -6439,8 +6559,9 @@ static void register_deref_usage(struct hlsl_ctx *ctx, const struct hlsl_deref * + else if (regset == HLSL_REGSET_NUMERIC) + { + type = hlsl_deref_get_type(ctx, deref); ++ VKD3D_ASSERT(type->class <= HLSL_CLASS_VECTOR); + +- required_bind_count = align(index + type->reg_size[regset], 4) / 4; ++ required_bind_count = align(index + type->e.numeric.dimx, 4) / 4; + var->bind_count[regset] = max(var->bind_count[regset], required_bind_count); + } + else +@@ -7753,6 +7874,10 @@ bool hlsl_regset_index_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref + *index += 4 * idx; + break; + ++ case HLSL_CLASS_VECTOR: ++ *index += idx; ++ break; ++ + default: + vkd3d_unreachable(); + } +@@ -8511,11 +8636,6 @@ static void remove_unreachable_code(struct hlsl_ctx *ctx, struct hlsl_block *bod + } + } + +-void hlsl_lower_index_loads(struct hlsl_ctx *ctx, struct hlsl_block *body) +-{ +- replace_ir(ctx, lower_index_loads, body); +-} +- + static enum hlsl_ir_expr_op invert_comparison_op(enum hlsl_ir_expr_op op) + { + switch (op) +@@ -13478,7 +13598,7 @@ static void generate_vsir_descriptors(struct hlsl_ctx *ctx, struct vsir_program + } + } + +- program->has_descriptor_info = true; ++ program->normalisation_flags.has_descriptor_info = true; + } + + /* For some reason, for matrices, values from default value initializers end +@@ -14927,7 +15047,7 @@ static void process_entry_function(struct hlsl_ctx *ctx, struct list *semantic_v + + replace_ir(ctx, lower_complex_casts, body); + replace_ir(ctx, lower_matrix_swizzles, body); +- replace_ir(ctx, lower_index_loads, body); ++ hlsl_lower_index_loads(ctx, body); + + replace_ir(ctx, lower_tgsm_loads, body); + replace_ir(ctx, lower_tgsm_stores, body); +diff --git a/libs/vkd3d/libs/vkd3d-shader/ir.c b/libs/vkd3d/libs/vkd3d-shader/ir.c +index 241006e32c6..1a0c9d83306 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/ir.c ++++ b/libs/vkd3d/libs/vkd3d-shader/ir.c +@@ -749,6 +749,21 @@ bool vsir_signature_find_sysval(const struct shader_signature *signature, + return false; + } + ++unsigned int vsir_signature_next_location(const struct shader_signature *signature) ++{ ++ unsigned int i, max_row; ++ ++ if (!signature) ++ return 0; ++ ++ 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; ++} ++ + struct vkd3d_shader_descriptor_info1 *vsir_program_add_descriptor(struct vsir_program *program, + enum vkd3d_shader_descriptor_type type, unsigned int register_id, + const struct vkd3d_shader_register_range *range, +@@ -2535,7 +2550,7 @@ static enum vkd3d_result vsir_program_lower_modifiers(struct vsir_program *progr + } + } + +- program->has_no_modifiers = true; ++ program->normalisation_flags.has_no_modifiers = true; + + return ret; + } +@@ -3064,11 +3079,6 @@ struct hull_flattener + unsigned int orig_ssa_count; + }; + +-static bool flattener_is_in_fork_or_join_phase(const struct hull_flattener *flattener) +-{ +- return flattener->phase == VSIR_OP_HS_FORK_PHASE || flattener->phase == VSIR_OP_HS_JOIN_PHASE; +-} +- + static void flattener_fixup_ssa_register(struct hull_flattener *normaliser, + struct vkd3d_shader_register *reg, unsigned int instance_id) + { +@@ -3190,9 +3200,9 @@ static enum vkd3d_result flattener_flatten_phases(struct hull_flattener *normali + normaliser->phase = VSIR_OP_INVALID; + for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it)) + { +- if (ins->opcode == VSIR_OP_HS_FORK_PHASE || ins->opcode == VSIR_OP_HS_JOIN_PHASE) ++ if (vsir_opcode_is_fork_or_join_phase(ins->opcode)) + { +- b = flattener_is_in_fork_or_join_phase(normaliser); ++ b = vsir_opcode_is_fork_or_join_phase(normaliser->phase); + /* Reset the phase info. */ + phase_body_it_valid = false; + normaliser->phase = ins->opcode; +@@ -3279,11 +3289,6 @@ struct control_point_normaliser + struct vkd3d_shader_src_param *outpointid_param; + }; + +-static bool control_point_normaliser_is_in_control_point_phase(const struct control_point_normaliser *normaliser) +-{ +- return normaliser->phase == VSIR_OP_HS_CONTROL_POINT_PHASE; +-} +- + struct vkd3d_shader_src_param *vsir_program_create_outpointid_param(struct vsir_program *program) + { + struct vkd3d_shader_src_param *rel_addr; +@@ -3303,7 +3308,7 @@ static void shader_dst_param_normalise_outpointid(struct vkd3d_shader_dst_param + { + struct vkd3d_shader_register *reg = &dst_param->reg; + +- if (control_point_normaliser_is_in_control_point_phase(normaliser) && reg->type == VKD3DSPR_OUTPUT) ++ if (vsir_opcode_is_control_point_phase(normaliser->phase) && reg->type == VKD3DSPR_OUTPUT) + { + /* The TPF reader validates idx_count. */ + VKD3D_ASSERT(reg->idx_count == 1); +@@ -3468,6 +3473,7 @@ struct io_normaliser + struct shader_signature *input_signature; + struct shader_signature *output_signature; + struct shader_signature *patch_constant_signature; ++ struct vsir_normalisation_flags *normalisation_flags; + + enum vkd3d_shader_opcode phase; + +@@ -3481,11 +3487,6 @@ struct io_normaliser + bool use_vocp; + }; + +-static bool io_normaliser_is_in_fork_or_join_phase(const struct io_normaliser *normaliser) +-{ +- return normaliser->phase == VSIR_OP_HS_FORK_PHASE || normaliser->phase == VSIR_OP_HS_JOIN_PHASE; +-} +- + static bool shader_signature_find_element_for_reg(const struct shader_signature *signature, + unsigned int reg_idx, unsigned int write_mask, unsigned int *element_idx) + { +@@ -3603,7 +3604,7 @@ static enum vkd3d_result io_normaliser_add_index_range(struct io_normaliser *nor + signature = normaliser->output_signature; + break; + case VKD3DSPR_OUTPUT: +- if (!io_normaliser_is_in_fork_or_join_phase(normaliser)) ++ if (!vsir_opcode_is_fork_or_join_phase(normaliser->phase)) + { + range_map = normaliser->output_range_map; + signature = normaliser->output_signature; +@@ -3889,7 +3890,7 @@ static bool shader_dst_param_io_normalise(struct vkd3d_shader_dst_param *dst_par + { + case VKD3DSPR_OUTPUT: + reg_idx = reg->idx[reg->idx_count - 1].offset; +- if (io_normaliser_is_in_fork_or_join_phase(normaliser)) ++ if (vsir_opcode_is_fork_or_join_phase(normaliser->phase)) + { + signature = normaliser->patch_constant_signature; + /* Convert patch constant outputs to the patch constant register type to avoid the need +@@ -3952,7 +3953,7 @@ static bool shader_dst_param_io_normalise(struct vkd3d_shader_dst_param *dst_par + vkd3d_unreachable(); + e = &signature->elements[element_idx]; + +- if ((e->register_count > 1 || vsir_sysval_semantic_is_tess_factor(e->sysval_semantic))) ++ if (vsir_signature_element_is_array(e, normaliser->normalisation_flags)) + id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index); + + /* Replace the register index with the signature element index */ +@@ -4005,7 +4006,7 @@ static void shader_src_param_io_normalise(struct vkd3d_shader_src_param *src_par + + case VKD3DSPR_OUTCONTROLPOINT: + reg->type = VKD3DSPR_OUTPUT; +- if (io_normaliser_is_in_fork_or_join_phase(normaliser)) ++ if (vsir_opcode_is_fork_or_join_phase(normaliser->phase)) + normaliser->use_vocp = true; + /* fall through */ + case VKD3DSPR_OUTPUT: +@@ -4034,7 +4035,7 @@ static void shader_src_param_io_normalise(struct vkd3d_shader_src_param *src_par + } + + e = &signature->elements[element_idx]; +- if ((e->register_count > 1 || vsir_sysval_semantic_is_tess_factor(e->sysval_semantic))) ++ if (vsir_signature_element_is_array(e, normaliser->normalisation_flags)) + id_idx = shader_register_normalise_arrayed_addressing(reg, id_idx, e->register_index); + reg->idx[id_idx].offset = element_idx; + reg->idx_count = id_idx + 1; +@@ -4089,6 +4090,7 @@ static enum vkd3d_result vsir_program_normalise_io_registers(struct vsir_program + normaliser.input_signature = &program->input_signature; + normaliser.output_signature = &program->output_signature; + normaliser.patch_constant_signature = &program->patch_constant_signature; ++ normaliser.normalisation_flags = &program->normalisation_flags; + + for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it)) + { +@@ -8091,9 +8093,9 @@ static enum vkd3d_result vsir_program_insert_clip_planes(struct vsir_program *pr + unsigned int low_signature_idx = ~0u, high_signature_idx = ~0u; + const struct vkd3d_shader_parameter1 *mask_parameter = NULL; + uint32_t position_signature_idx, position_temp, mask; ++ unsigned int plane_count, next_register_index; + struct signature_element *clip_element; + struct vkd3d_shader_instruction *ins; +- unsigned int plane_count; + int ret; + + if (program->shader_version.type != VKD3D_SHADER_TYPE_VERTEX) +@@ -8149,16 +8151,18 @@ static enum vkd3d_result vsir_program_insert_clip_planes(struct vsir_program *pr + plane_count = vkd3d_popcount(mask); + + /* Register mask is ignored since we operate after I/O normalisation. */ ++ next_register_index = vsir_signature_next_location(signature); + if (!(clip_element = add_signature_element(signature, "SV_ClipDistance", 0, +- vkd3d_write_mask_from_component_count(min(plane_count, 4)), 0, VKD3DSIM_NONE))) ++ vkd3d_write_mask_from_component_count(min(plane_count, 4)), next_register_index, VKD3DSIM_NONE))) + return VKD3D_ERROR_OUT_OF_MEMORY; + low_signature_idx = clip_element - signature->elements; + clip_element->sysval_semantic = VKD3D_SHADER_SV_CLIP_DISTANCE; + + if (plane_count > 4) + { ++ next_register_index = vsir_signature_next_location(signature); + if (!(clip_element = add_signature_element(signature, "SV_ClipDistance", 1, +- vkd3d_write_mask_from_component_count(plane_count - 4), 0, VKD3DSIM_NONE))) ++ vkd3d_write_mask_from_component_count(plane_count - 4), next_register_index, VKD3DSIM_NONE))) + return VKD3D_ERROR_OUT_OF_MEMORY; + high_signature_idx = clip_element - signature->elements; + clip_element->sysval_semantic = VKD3D_SHADER_SV_CLIP_DISTANCE; +@@ -8198,6 +8202,742 @@ static enum vkd3d_result vsir_program_insert_clip_planes(struct vsir_program *pr + return VKD3D_OK; + } + ++struct sysval_array_normaliser ++{ ++ struct vsir_transformation_context *ctx; ++ ++ /* sysval semantic currently being normalised. */ ++ enum vkd3d_shader_sysval_semantic sysval_semantic; ++ bool output; ++ ++ /* Registers used by the sysval elements of the original signature. */ ++ struct ++ { ++ unsigned int index; ++ unsigned int mask; ++ } regs[2]; ++ unsigned int reg_count; ++ ++ /* Index of the signature element created for the new array. */ ++ unsigned int element_idx; ++ /* Indexable temporary reserved to store a copy of the native sysval ++ * values for the current phase. If ~0u, the temporary has not been ++ * allocated for this phase yet. */ ++ unsigned int idxtemp_idx; ++ ++ enum vkd3d_shader_opcode phase; ++}; ++ ++static enum vkd3d_result sysval_array_normaliser_add_components( ++ struct sysval_array_normaliser *normaliser, unsigned int index, unsigned int mask) ++{ ++ unsigned int q; ++ ++ for (q = 0; q < normaliser->reg_count; ++q) ++ { ++ if (index == normaliser->regs[q].index) ++ break; ++ } ++ ++ if (q == normaliser->reg_count) ++ { ++ if (normaliser->reg_count >= ARRAY_SIZE(normaliser->regs)) ++ { ++ vkd3d_shader_error(normaliser->ctx->message_context, ++ &normaliser->ctx->null_location, VKD3D_SHADER_ERROR_VSIR_INVALID_SIGNATURE, ++ "Sysval semantic %#x elements require more than %zu registers.\n", ++ normaliser->sysval_semantic, ARRAY_SIZE(normaliser->regs)); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ normaliser->reg_count += 1; ++ } ++ normaliser->regs[q].index = index; ++ normaliser->regs[q].mask |= mask; ++ ++ return VKD3D_OK; ++} ++ ++static enum vkd3d_result sysval_array_normaliser_init(struct vsir_transformation_context *ctx, ++ const char *semantic_name, enum vkd3d_shader_sysval_semantic sysval_semantic, ++ bool output, struct sysval_array_normaliser *normaliser) ++{ ++ unsigned int component_count = 0, next_register_index; ++ struct shader_signature *signature; ++ struct signature_element *element; ++ enum vkd3d_result res; ++ ++ memset(normaliser, 0, sizeof(*normaliser)); ++ normaliser->ctx = ctx; ++ normaliser->sysval_semantic = sysval_semantic; ++ normaliser->output = output; ++ normaliser->element_idx = ~0u; ++ ++ normaliser->phase = VSIR_OP_INVALID; ++ ++ signature = output ? &ctx->program->output_signature : &ctx->program->input_signature; ++ ++ for (unsigned int i = 0; i < signature->element_count; ++i) ++ { ++ element = &signature->elements[i]; ++ if (element->sysval_semantic != sysval_semantic) ++ continue; ++ ++ for (unsigned int j = 0; j < element->register_count; ++j) ++ { ++ if ((res = sysval_array_normaliser_add_components(normaliser, ++ element->register_index + j, element->mask)) < 0) ++ return res; ++ } ++ } ++ ++ if (!normaliser->reg_count) ++ return VKD3D_OK; ++ next_register_index = vsir_signature_next_location(signature); ++ if (!(element = add_signature_element(signature, semantic_name, next_register_index, ++ VKD3DSP_WRITEMASK_0, signature->element_count, element->interpolation_mode))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ element->sysval_semantic = sysval_semantic; ++ for (unsigned int q = 0; q < normaliser->reg_count; ++q) ++ { ++ component_count += vkd3d_popcount(normaliser->regs[q].mask); ++ } ++ element->register_count = component_count; ++ normaliser->element_idx = signature->element_count - 1; ++ ++ return VKD3D_OK; ++} ++ ++/* For every component 'k' that belongs to an output signature element that ++ * has the sysval currently being handled by the sysval_array_normaliser, add ++ * the following instruction before the return points of the program: ++ * ++ * mov o[k][e].x, x[idxtmp_idx][q].kkkk ++ * ++ * or in case this is the control point phase of a hull shader: ++ * ++ * mov o[k][P][e].x, x[idxtmp_idx][q].kkkk ++ * ++ * where: ++ * 'q' is the index of the register containing 'k' in the normaliser's ++ * internal list. ++ * '.kkkk' is the replicated swizzle that corresponds to component 'k'. ++ * 'e' is the new array's signature element index. ++ * 'idxtmp_idx' is the index of the indexable temp reserved by the ++ * normaliser. ++ * 'P' is the output control point ID. ++ */ ++static enum vkd3d_result sysval_array_normaliser_add_output_copy( ++ struct sysval_array_normaliser *normaliser, struct vsir_program_iterator *it) ++{ ++ struct vsir_program *program = normaliser->ctx->program; ++ struct vkd3d_shader_src_param *outpointid_param = NULL; ++ unsigned int output_component_count = 0; ++ struct vkd3d_shader_instruction *mov; ++ struct signature_element *element; ++ struct vkd3d_shader_location loc; ++ ++ if (!normaliser->output) ++ return VKD3D_OK; ++ if (vsir_opcode_is_fork_or_join_phase(normaliser->phase)) ++ return VKD3D_OK; ++ if (normaliser->idxtemp_idx == ~0u) ++ return VKD3D_OK; ++ ++ element = &program->output_signature.elements[normaliser->element_idx]; ++ loc = vsir_program_iterator_current(it)->location; ++ ++ if (program->shader_version.type == VKD3D_SHADER_TYPE_HULL ++ && !(outpointid_param = vsir_program_create_outpointid_param(program))) ++ { ++ ERR("Failed to allocate outpointid param.\n"); ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ } ++ ++ for (unsigned int q = 0; q < normaliser->reg_count; ++q) ++ { ++ for (unsigned int k = 0; k < VKD3D_VEC4_SIZE; ++k) ++ { ++ struct vkd3d_shader_dst_param *dst; ++ struct vkd3d_shader_src_param *src; ++ ++ if (!(normaliser->regs[q].mask & (1u << k))) ++ continue; ++ ++ if (!(mov = vsir_program_iterator_insert_before_and_move(it, 1))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ if (!vsir_instruction_init_with_params(program, mov, &loc, VSIR_OP_MOV, 1, 1)) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ dst = &mov->dst[0]; ++ vsir_dst_param_init(dst, VKD3DSPR_OUTPUT, VSIR_DATA_F32, 2); ++ dst->reg.idx[0].offset = output_component_count++; ++ dst->reg.idx[1].offset = normaliser->element_idx; ++ dst->reg.dimension = VSIR_DIMENSION_VEC4; ++ dst->write_mask = VKD3DSP_WRITEMASK_0; ++ if (outpointid_param) ++ { ++ dst->reg.idx_count = 3; ++ dst->reg.idx[2] = dst->reg.idx[1]; ++ dst->reg.idx[1].rel_addr = outpointid_param; ++ dst->reg.idx[1].offset = 0; ++ } ++ ++ src = &mov->src[0]; ++ vsir_src_param_init(src, VKD3DSPR_IDXTEMP, VSIR_DATA_F32, 2); ++ src->reg.idx[0].offset = normaliser->idxtemp_idx; ++ src->reg.idx[1].offset = q; ++ src->reg.dimension = VSIR_DIMENSION_VEC4; ++ src->swizzle = vsir_swizzle_from_writemask(1u << k); ++ ++ vsir_program_iterator_next(it); ++ } ++ } ++ VKD3D_ASSERT(output_component_count == element->register_count); ++ ++ return VKD3D_OK; ++} ++ ++/* For every component 'k' that belongs to an input signature element that has ++ * the sysval currently being handled by the sysval_array_normaliser, add the ++ * following single instruction at the beginning of the program: ++ * ++ * mov x[idxtmp_idx][q].k, v[k][e].x ++ * ++ * or in case there are multiple input control points, add multiple ++ * instructions, one for every one of them 'p': ++ * ++ * mov x[idxtmp_idx][p * reg_count + q].k, v[k][p][e].x ++ * ++ * where: ++ * 'q' is the index of the register containing 'k' in the normaliser's ++ * internal list. ++ * '.k' is the write mask that corresponds to component 'k' ++ * 'e' is the new array's signature element index. ++ * 'idxtmp_idx' is the index of the indexable temp reserved by the ++ * normaliser. ++ * 'reg_count' is the number of registers in the normaliser's internal ++ * list. ++ * ++ * NOTE: This function also does this for components 'k' that belong to an ++ * output signature in case the normaliser is handling an output semantic and ++ * this is the fork or join phase of a hull shader, where they can be used as ++ * source operands. Naturally, 'o' registers are used as source operands on ++ * such 'mov' instructions instead of 'v'. ++ */ ++static enum vkd3d_result sysval_array_normaliser_add_input_copy( ++ struct sysval_array_normaliser *normaliser, struct vsir_program_iterator *it) ++{ ++ struct vsir_program *program = normaliser->ctx->program; ++ struct vkd3d_shader_instruction *mov; ++ struct signature_element *element; ++ unsigned int control_point_count; ++ struct vkd3d_shader_location loc; ++ ++ loc = vsir_program_iterator_current(it)->location; ++ if (normaliser->output) ++ { ++ control_point_count = program->output_control_point_count; ++ element = &program->output_signature.elements[normaliser->element_idx]; ++ } ++ else ++ { ++ control_point_count = program->input_control_point_count; ++ element = &program->input_signature.elements[normaliser->element_idx]; ++ } ++ ++ if (!vsir_program_iterator_insert_before_and_move(it, max(1, control_point_count) * element->register_count)) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ for (unsigned int p = 0; p < max(1, control_point_count); ++p) ++ { ++ unsigned int input_component_count = 0; ++ ++ for (unsigned int q = 0; q < normaliser->reg_count; ++q) ++ { ++ for (unsigned int k = 0; k < VKD3D_VEC4_SIZE; ++k) ++ { ++ struct vkd3d_shader_dst_param *dst; ++ struct vkd3d_shader_src_param *src; ++ ++ if (!(normaliser->regs[q].mask & (1u << k))) ++ continue; ++ ++ mov = vsir_program_iterator_current(it); ++ vsir_instruction_init_with_params(program, mov, &loc, VSIR_OP_MOV, 1, 1); ++ ++ dst = &mov->dst[0]; ++ vsir_dst_param_init(dst, VKD3DSPR_IDXTEMP, VSIR_DATA_F32, 2); ++ dst->reg.idx[0].offset = normaliser->idxtemp_idx; ++ dst->reg.idx[1].offset = p * normaliser->reg_count + q; ++ dst->reg.dimension = VSIR_DIMENSION_VEC4; ++ dst->write_mask = 1u << k; ++ ++ src = &mov->src[0]; ++ if (control_point_count) ++ { ++ vsir_src_param_init(src, normaliser->output ? VKD3DSPR_OUTPUT : VKD3DSPR_INPUT, VSIR_DATA_F32, 3); ++ src->reg.idx[0].offset = input_component_count++; ++ src->reg.idx[1].offset = p; ++ src->reg.idx[2].offset = normaliser->element_idx; ++ } ++ else ++ { ++ vsir_src_param_init(src, VKD3DSPR_INPUT, VSIR_DATA_F32, 2); ++ src->reg.idx[0].offset = input_component_count++; ++ src->reg.idx[1].offset = normaliser->element_idx; ++ } ++ src->reg.dimension = VSIR_DIMENSION_VEC4; ++ src->swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); ++ ++ vsir_program_iterator_next(it); ++ } ++ } ++ VKD3D_ASSERT(input_component_count == element->register_count); ++ } ++ ++ return VKD3D_OK; ++} ++ ++/* NOTE: This might be replaced by a single field in vsir_program at some point. */ ++static unsigned int vsir_program_get_idxtemp_count(struct vsir_program *program) ++{ ++ struct vsir_program_iterator it = vsir_program_iterator(&program->instructions); ++ struct vkd3d_shader_instruction *ins; ++ size_t count = 0; ++ ++ for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it)) ++ { ++ if (ins->opcode != VSIR_OP_DCL_INDEXABLE_TEMP) ++ continue; ++ if (count < ins->declaration.indexable_temp.register_idx) ++ count = ins->declaration.indexable_temp.register_idx; ++ } ++ ++ return count; ++} ++ ++static enum vkd3d_result sysval_array_normaliser_dcl_indexable_temp( ++ struct sysval_array_normaliser *normaliser, struct vsir_program_iterator *it, size_t idx) ++{ ++ struct vsir_program *program = normaliser->ctx->program; ++ unsigned int register_size = normaliser->reg_count; ++ struct vkd3d_shader_indexable_temp *t; ++ struct vkd3d_shader_instruction *ins; ++ unsigned int control_point_count; ++ ++ normaliser->idxtemp_idx = idx; ++ control_point_count = normaliser->output ++ ? program->output_control_point_count : program->input_control_point_count; ++ ++ if (control_point_count && (!normaliser->output || vsir_opcode_is_fork_or_join_phase(normaliser->phase))) ++ register_size *= program->input_control_point_count; ++ ++ if (!(ins = vsir_program_iterator_insert_before_and_move(it, 1))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ vsir_instruction_init_with_params(program, ins, &normaliser->ctx->null_location, VSIR_OP_DCL_INDEXABLE_TEMP, 0, 0); ++ t = &ins->declaration.indexable_temp; ++ t->register_idx = normaliser->idxtemp_idx; ++ t->register_size = register_size; ++ t->alignment = 0; ++ t->data_type = VSIR_DATA_F32; ++ t->component_count = 4; ++ t->has_function_scope = false; ++ ++ vsir_program_iterator_next(it); ++ ++ return VKD3D_OK; ++} ++ ++static bool vsir_program_validate_outpointid_control_point_index(const struct vkd3d_shader_register *reg) ++{ ++ const struct vkd3d_shader_register_index *index; ++ ++ if (reg->idx_count < 2) ++ return false; ++ ++ index = ®->idx[reg->idx_count - 2]; ++ if (index->offset) ++ return false; ++ if (!index->rel_addr || index->rel_addr->reg.type != VKD3DSPR_OUTPOINTID) ++ return false; ++ if (index->rel_addr->reg.idx_count) ++ return false; ++ return true; ++} ++ ++/* If a register refers to a signature element of index 'e' that has the ++ * sysval being handled by the normaliser, this maps the register as follows: ++ * ++ * v[e] -> x[idxtmp_idx][q] ++ * ++ * v[i][e] -> x[idxtmp_idx][i + q] ++ * on shaders without control points. ++ * ++ * v[p][e] -> x[idxtmp_idx][p * reg_count + q], ++ * on shaders with control points. ++ * ++ * v[i][p][e] -> x[idxtmp_idx][p * reg_count + i + q] ++ * on shaders with control points. ++ * ++ * o[e] -> x[idxtmp_idx][q] ++ * ++ * o[i][e] -> x[idxtmp_idx][i + q] ++ * on shaders without control points. ++ * ++ * o[p][e] -> x[idxtmp_idx][p * reg_count + q] ++ * if on HS fork/join phase, where it is a src. ++ * ++ * o[P][e] -> x[idxtmp_idx][q] ++ * if on HS control point phase, where it is a dst. ++ * P is expected to always be the output control point ID. ++ * ++ * o[i][p][e] -> x[idxtmp_idx][p * reg_count + i + q] ++ * if on HS fork/join phase, where it is a src. ++ * ++ * o[i][P][e] -> x[idxtmp_idx][i + q] ++ * if on HS control point phase, where it is a dst. ++ * P is expected to always be the output control point ID. ++ * ++ * where: ++ * 'q' is the index of the register that matches signature element 'e' in ++ * the normaliser's internal list. ++ * 'idxtmp_idx' is the index of the indexable temp reserved by the ++ * normaliser. ++ * 'reg_count' is the number of registers in the normaliser's internal ++ * list. ++ * ++ * The swizzle (for source operands) is also combined with the mask of the ++ * relevant signature element 'e'. ++ */ ++static enum vkd3d_result sysval_array_normaliser_map_register(struct sysval_array_normaliser *normaliser, ++ struct vsir_program_iterator *it, struct vkd3d_shader_register *reg, unsigned int *src_swizzle) ++{ ++ struct vkd3d_shader_register_index i_idx = {0}, p_idx = {0}; ++ struct vsir_program *program = normaliser->ctx->program; ++ unsigned int element_index, control_point_count; ++ struct vkd3d_shader_instruction *ssa_ins; ++ struct shader_signature *signature; ++ struct signature_element *element; ++ struct vkd3d_shader_location loc; ++ unsigned int q; ++ ++ loc = vsir_program_iterator_current(it)->location; ++ ++ signature = normaliser->output ? &program->output_signature : &program->input_signature; ++ control_point_count = normaliser->output ? program->output_control_point_count ++ : program->input_control_point_count; ++ ++ for (unsigned int i = 0; i < reg->idx_count; ++i) ++ { ++ if (reg->idx[i].rel_addr) ++ sysval_array_normaliser_map_register(normaliser, it, ++ ®->idx[i].rel_addr->reg, ®->idx[i].rel_addr->swizzle); ++ } ++ ++ if (normaliser->output && reg->type != VKD3DSPR_OUTPUT) ++ return VKD3D_OK; ++ if (!normaliser->output && reg->type != VKD3DSPR_INPUT) ++ return VKD3D_OK; ++ ++ element_index = reg->idx[reg->idx_count - 1].offset; ++ element = &signature->elements[element_index]; ++ if (element->sysval_semantic != normaliser->sysval_semantic) ++ return VKD3D_OK; ++ ++ for (q = 0; q < normaliser->reg_count; ++q) ++ { ++ if (normaliser->regs[q].index == element->register_index) ++ break; ++ } ++ VKD3D_ASSERT(q < normaliser->reg_count); ++ ++ if (normaliser->output && normaliser->phase == VSIR_OP_HS_CONTROL_POINT_PHASE) ++ { ++ if (!vsir_program_validate_outpointid_control_point_index(reg)) ++ vkd3d_shader_error(normaliser->ctx->message_context, &loc, VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX, ++ "Control point index of output source operand is not OUTPOINTID.\n"); ++ } ++ ++ if (control_point_count) ++ { ++ if (reg->idx_count == 3) ++ { ++ i_idx = reg->idx[0]; ++ p_idx = reg->idx[1]; ++ } ++ else ++ { ++ p_idx = reg->idx[0]; ++ } ++ } ++ else if (reg->idx_count == 2) ++ { ++ i_idx = reg->idx[0]; ++ } ++ ++ reg->type = VKD3DSPR_IDXTEMP; ++ reg->idx[0].offset = normaliser->idxtemp_idx; ++ reg->idx[0].rel_addr = NULL; ++ reg->idx_count = 2; ++ ++ if (p_idx.rel_addr && !(normaliser->output && normaliser->phase == VSIR_OP_HS_CONTROL_POINT_PHASE)) ++ { ++ if (!(ssa_ins = vsir_program_iterator_insert_before_and_move(it, 1 + !!i_idx.rel_addr))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ if (!vsir_instruction_init_with_params(program, ssa_ins, &loc, VSIR_OP_IMUL_LOW, 1, 2)) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ vsir_register_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1); ++ ssa_ins->dst[0].reg.idx[0].offset = program->ssa_count++; ++ ssa_ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; ++ ssa_ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0; ++ ssa_ins->src[0] = *p_idx.rel_addr; ++ src_param_init_const_uint(&ssa_ins->src[1], normaliser->reg_count); ++ ++ if (i_idx.rel_addr) ++ { ++ ssa_ins = vsir_program_iterator_next(it); ++ if (!vsir_instruction_init_with_params(program, ssa_ins, &loc, VSIR_OP_ADD, 1, 2)) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ ++ vsir_register_init(&ssa_ins->dst[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1); ++ ssa_ins->dst[0].reg.idx[0].offset = program->ssa_count++; ++ ssa_ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; ++ ssa_ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0; ++ vsir_register_init(&ssa_ins->src[0].reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1); ++ ssa_ins->src[0].reg.idx[0].offset = program->ssa_count - 2; ++ ssa_ins->src[1] = *i_idx.rel_addr; ++ } ++ ++ vsir_program_iterator_next(it); ++ ++ reg->idx[1].offset = normaliser->reg_count * p_idx.offset + i_idx.offset + q; ++ if (!(reg->idx[1].rel_addr = vsir_program_get_src_params(program, 1))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ vsir_register_init(®->idx[1].rel_addr->reg, VKD3DSPR_SSA, VSIR_DATA_U32, 1); ++ reg->idx[1].rel_addr->reg.idx[0].offset = program->ssa_count - 1; ++ reg->idx[1].rel_addr->reg.dimension = VSIR_DIMENSION_VEC4; ++ reg->idx[1].rel_addr->swizzle = VKD3D_SHADER_SWIZZLE_X; ++ reg->idx[1].rel_addr->modifiers = 0; ++ } ++ else ++ { ++ reg->idx[1].offset = normaliser->reg_count * p_idx.offset + i_idx.offset + q; ++ reg->idx[1].rel_addr = i_idx.rel_addr; ++ } ++ ++ if (src_swizzle) ++ *src_swizzle = vsir_combine_swizzles(vsir_swizzle_from_writemask(element->mask), *src_swizzle); ++ ++ return VKD3D_OK; ++} ++ ++static enum vkd3d_result sysval_array_normaliser_map_instruction( ++ struct sysval_array_normaliser *normaliser, struct vsir_program_iterator *it) ++{ ++ struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(it); ++ unsigned int src_count, dst_count; ++ enum vkd3d_result res; ++ ++ if (vsir_instruction_is_dcl(ins)) ++ return VKD3D_OK; ++ ++ dst_count = ins->dst_count; ++ src_count = ins->src_count; ++ ++ for (unsigned int k = 0; k < dst_count; ++k) ++ { ++ ins = vsir_program_iterator_current(it); ++ if ((res = sysval_array_normaliser_map_register(normaliser, it, &ins->dst[k].reg, NULL))) ++ return res; ++ } ++ ++ for (unsigned int k = 0; k < src_count; ++k) ++ { ++ ins = vsir_program_iterator_current(it); ++ if ((res = sysval_array_normaliser_map_register(normaliser, it, &ins->src[k].reg, &ins->src[k].swizzle))) ++ return res; ++ } ++ ++ return VKD3D_OK; ++} ++ ++static void shader_register_remove_signature_element(struct vkd3d_shader_register *reg, ++ enum vkd3d_shader_register_type type, unsigned int index) ++{ ++ unsigned int current_idx; ++ ++ for (unsigned int i = 0; i < reg->idx_count; ++i) ++ { ++ if (reg->idx[i].rel_addr) ++ shader_register_remove_signature_element(®->idx[i].rel_addr->reg, type, index); ++ } ++ ++ if (reg->type != type) ++ return; ++ ++ VKD3D_ASSERT(!reg->idx[reg->idx_count - 1].rel_addr); ++ current_idx = reg->idx[reg->idx_count - 1].offset; ++ VKD3D_ASSERT(current_idx != index); ++ if (current_idx > index) ++ --reg->idx[reg->idx_count - 1].offset; ++} ++ ++static void vsir_program_remove_signature_element(struct vsir_program *program, ++ enum vkd3d_shader_register_type type, unsigned int index) ++{ ++ struct vsir_program_iterator it = vsir_program_iterator(&program->instructions); ++ struct vkd3d_shader_instruction *ins; ++ struct shader_signature *signature; ++ ++ switch (type) ++ { ++ case VKD3DSPR_INPUT: ++ signature = &program->input_signature; ++ break; ++ case VKD3DSPR_OUTPUT: ++ signature = &program->output_signature; ++ break; ++ case VKD3DSPR_PATCHCONST: ++ signature = &program->patch_constant_signature; ++ break; ++ default: ++ vkd3d_unreachable(); ++ } ++ ++ for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it)) ++ { ++ if (vsir_instruction_is_dcl(ins)) ++ continue; ++ for (unsigned int i = 0; i < ins->dst_count; ++i) ++ shader_register_remove_signature_element(&ins->dst[i].reg, type, index); ++ for (unsigned int i = 0; i < ins->src_count; ++i) ++ shader_register_remove_signature_element(&ins->src[i].reg, type, index); ++ } ++ ++ memmove(&signature->elements[index], &signature->elements[index + 1], ++ sizeof(*signature->elements) * (signature->element_count - 1 - index)); ++ --signature->element_count; ++} ++ ++static void sysval_array_normaliser_remove_old_signature_elements(struct sysval_array_normaliser *normaliser) ++{ ++ struct vsir_program *program = normaliser->ctx->program; ++ enum vkd3d_shader_register_type type; ++ struct shader_signature *signature; ++ struct signature_element *element; ++ ++ signature = normaliser->output ? &program->output_signature : &program->input_signature; ++ type = normaliser->output ? VKD3DSPR_OUTPUT : VKD3DSPR_INPUT; ++ ++ for (int i = signature->element_count - 2; i >= 0; --i) ++ { ++ element = &signature->elements[i]; ++ if (element->sysval_semantic != normaliser->sysval_semantic) ++ continue; ++ TRACE("Removing %s signature element index %u.\n", normaliser->output ? "output" : "input", i); ++ vsir_program_remove_signature_element(program, type, i); ++ } ++} ++ ++static enum vkd3d_result vsir_program_normalise_sysval_array(struct vsir_transformation_context *ctx, ++ const char *semantic_name, enum vkd3d_shader_sysval_semantic sysval_semantic, bool output) ++{ ++ struct vsir_program *program = ctx->program; ++ struct sysval_array_normaliser normaliser; ++ struct vkd3d_shader_instruction *ins; ++ struct vsir_program_iterator it; ++ bool declarations = true; ++ enum vkd3d_result res; ++ ++ if ((res = sysval_array_normaliser_init(ctx, semantic_name, sysval_semantic, output, &normaliser)) < 0) ++ return res; ++ ++ if (!normaliser.reg_count) ++ return VKD3D_OK; ++ ++ if (!output && program->shader_version.type == VKD3D_SHADER_TYPE_VERTEX) ++ return VKD3D_OK; ++ ++ if (TRACE_ON()) ++ vsir_program_trace(program); ++ ++ it = vsir_program_iterator(&program->instructions); ++ for (ins = vsir_program_iterator_head(&it); ins; ins = vsir_program_iterator_next(&it)) ++ { ++ if (ins->opcode == VSIR_OP_HS_DECLS || ins->opcode == VSIR_OP_HS_CONTROL_POINT_PHASE ++ || ins->opcode == VSIR_OP_HS_FORK_PHASE || ins->opcode == VSIR_OP_HS_JOIN_PHASE) ++ { ++ normaliser.phase = ins->opcode; ++ declarations = true; ++ continue; ++ } ++ ++ if (declarations && !vsir_instruction_is_dcl(ins) && ins->opcode != VSIR_OP_NOP) ++ { ++ unsigned int idxtemp_idx = vsir_program_get_idxtemp_count(program) + 1; ++ ++ declarations = false; ++ ++ if ((res = sysval_array_normaliser_dcl_indexable_temp(&normaliser, &it, idxtemp_idx)) < 0) ++ return res; ++ ++ if (vsir_program_iterator_current(&it)->opcode == VSIR_OP_LABEL) ++ ins = vsir_program_iterator_next(&it); ++ ++ if ((!output || vsir_opcode_is_fork_or_join_phase(normaliser.phase)) ++ && (res = sysval_array_normaliser_add_input_copy(&normaliser, &it)) < 0) ++ return res; ++ } ++ ++ if (!declarations) ++ { ++ if (ins->opcode == VSIR_OP_RET || ins->opcode == VSIR_OP_EMIT || ins->opcode == VSIR_OP_EMIT_STREAM) ++ { ++ if ((output && !vsir_opcode_is_fork_or_join_phase(normaliser.phase)) ++ && (res = sysval_array_normaliser_add_output_copy(&normaliser, &it)) < 0) ++ return res; ++ } ++ else ++ { ++ if ((res = sysval_array_normaliser_map_instruction(&normaliser, &it)) < 0) ++ return res; ++ } ++ } ++ } ++ VKD3D_ASSERT(!declarations); ++ if (TRACE_ON()) ++ vsir_program_trace(program); ++ sysval_array_normaliser_remove_old_signature_elements(&normaliser); ++ ++ return VKD3D_OK; ++} ++ ++/* This pass transform clip/cull system values from the Direct3D convention of ++ * 2 4-component registers, into the SPIR-V/GLSL convention of 8-element ++ * scalar float arrays. */ ++static enum vkd3d_result vsir_program_normalise_clip_cull( ++ struct vsir_program *program, struct vsir_transformation_context *ctx) ++{ ++ enum vkd3d_result res; ++ ++ if ((res = vsir_program_normalise_sysval_array(ctx, "SV_ClipDistance", VKD3D_SHADER_SV_CLIP_DISTANCE, false)) < 0) ++ return res; ++ if ((res = vsir_program_normalise_sysval_array(ctx, "SV_ClipDistance", VKD3D_SHADER_SV_CLIP_DISTANCE, true)) < 0) ++ return res; ++ if ((res = vsir_program_normalise_sysval_array(ctx, "SV_CullDistance", VKD3D_SHADER_SV_CULL_DISTANCE, false)) < 0) ++ return res; ++ if ((res = vsir_program_normalise_sysval_array(ctx, "SV_CullDistance", VKD3D_SHADER_SV_CULL_DISTANCE, true)) < 0) ++ return res; ++ ++ program->normalisation_flags.normalised_clip_cull_arrays = true; ++ ++ return VKD3D_OK; ++} ++ + static bool is_pre_rasterization_shader(enum vkd3d_shader_type type) + { + return type == VKD3D_SHADER_TYPE_VERTEX +@@ -10527,8 +11267,7 @@ static void vsir_validate_io_register(struct validation_context *ctx, const stru + } + + element = &signature->elements[signature_idx]; +- if (element->register_count > 1 || vsir_sysval_semantic_is_tess_factor(element->sysval_semantic)) +- is_array = true; ++ is_array = vsir_signature_element_is_array(element, &ctx->program->normalisation_flags); + + expected_idx_count = 1 + !!has_control_point + !!is_array; + control_point_index = !!is_array; +@@ -10721,7 +11460,7 @@ static void vsir_validate_descriptor_indices(struct validation_context *ctx, + validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_INDEX, + "Non-NULL indirect address for the ID of a register of type \"%s\".", name); + +- if (!ctx->program->has_descriptor_info) ++ if (!ctx->program->normalisation_flags.has_descriptor_info) + return; + + if (!(descriptor = vkd3d_shader_find_descriptor(&ctx->program->descriptors, type, reg->idx[0].offset))) +@@ -11158,7 +11897,7 @@ static void vsir_validate_dst_param(struct validation_context *ctx, + break; + } + +- if (dst->modifiers & ~VKD3DSPDM_MASK || (ctx->program->has_no_modifiers && dst->modifiers)) ++ if (dst->modifiers & ~VKD3DSPDM_MASK || (ctx->program->normalisation_flags.has_no_modifiers && dst->modifiers)) + validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_MODIFIERS, "Destination has invalid modifiers %#x.", + dst->modifiers); + +@@ -11339,7 +12078,7 @@ static void vsir_validate_src_param(struct validation_context *ctx, + validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_SWIZZLE, + "Immediate constant source has invalid swizzle %#x.", src->swizzle); + +- if (src->modifiers >= VKD3DSPSM_COUNT || (ctx->program->has_no_modifiers && src->modifiers)) ++ if (src->modifiers >= VKD3DSPSM_COUNT || (ctx->program->normalisation_flags.has_no_modifiers && src->modifiers)) + validator_error(ctx, VKD3D_SHADER_ERROR_VSIR_INVALID_MODIFIERS, "Source has invalid modifiers %#x.", + src->modifiers); + +@@ -14242,6 +14981,7 @@ enum vkd3d_result vsir_program_transform(struct vsir_program *program, uint64_t + vsir_transform(&ctx, vsir_program_apply_flat_interpolation); + vsir_transform(&ctx, vsir_program_insert_alpha_test); + vsir_transform(&ctx, vsir_program_insert_clip_planes); ++ vsir_transform(&ctx, vsir_program_normalise_clip_cull); + vsir_transform(&ctx, vsir_program_insert_point_size); + vsir_transform(&ctx, vsir_program_insert_point_size_clamp); + vsir_transform(&ctx, vsir_program_insert_point_coord); +diff --git a/libs/vkd3d/libs/vkd3d-shader/msl.c b/libs/vkd3d/libs/vkd3d-shader/msl.c +index d34133d6d4c..2a6a243ee9f 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/msl.c ++++ b/libs/vkd3d/libs/vkd3d-shader/msl.c +@@ -2401,8 +2401,8 @@ int msl_compile(struct vsir_program *program, uint64_t config_flags, + return ret; + + VKD3D_ASSERT(program->normalisation_level == VSIR_NORMALISED_SM6); +- VKD3D_ASSERT(program->has_descriptor_info); +- VKD3D_ASSERT(program->has_no_modifiers); ++ VKD3D_ASSERT(program->normalisation_flags.has_descriptor_info); ++ VKD3D_ASSERT(program->normalisation_flags.has_no_modifiers); + + if ((ret = msl_generator_init(&generator, program, compile_info, message_context)) < 0) + return ret; +diff --git a/libs/vkd3d/libs/vkd3d-shader/spirv.c b/libs/vkd3d/libs/vkd3d-shader/spirv.c +index 0d260d63542..83cc0eb18a5 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/spirv.c ++++ b/libs/vkd3d/libs/vkd3d-shader/spirv.c +@@ -2972,7 +2972,6 @@ struct spirv_compiler + { + uint32_t id; + enum vsir_data_type data_type; +- uint32_t array_element_mask; + } *output_info; + uint32_t private_output_variable[MAX_REG_OUTPUT + 1]; /* 1 entry for oDepth */ + uint32_t private_output_variable_write_mask[MAX_REG_OUTPUT + 1]; /* 1 entry for oDepth */ +@@ -3019,16 +3018,6 @@ static bool is_in_default_phase(const struct spirv_compiler *compiler) + return compiler->phase == VSIR_OP_INVALID; + } + +-static bool is_in_control_point_phase(const struct spirv_compiler *compiler) +-{ +- return compiler->phase == VSIR_OP_HS_CONTROL_POINT_PHASE; +-} +- +-static bool is_in_fork_or_join_phase(const struct spirv_compiler *compiler) +-{ +- return compiler->phase == VSIR_OP_HS_FORK_PHASE || compiler->phase == VSIR_OP_HS_JOIN_PHASE; +-} +- + static void spirv_compiler_emit_initial_declarations(struct spirv_compiler *compiler); + static size_t spirv_compiler_get_current_function_location(struct spirv_compiler *compiler); + static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler); +@@ -5462,7 +5451,8 @@ static const struct vkd3d_shader_phase *spirv_compiler_get_current_shader_phase( + if (is_in_default_phase(compiler)) + return NULL; + +- return is_in_control_point_phase(compiler) ? &compiler->control_point_phase : &compiler->patch_constant_phase; ++ return vsir_opcode_is_control_point_phase(compiler->phase) ++ ? &compiler->control_point_phase : &compiler->patch_constant_phase; + } + + static void spirv_compiler_decorate_xfb_output(struct spirv_compiler *compiler, +@@ -5561,18 +5551,6 @@ static bool needs_private_io_variable(const struct vkd3d_spirv_builtin *builtin) + return builtin && builtin->fixup_pfn; + } + +-static unsigned int shader_signature_next_location(const struct shader_signature *signature) +-{ +- unsigned int i, max_row; +- +- if (!signature) +- return 0; +- +- 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; +-} +- + static const struct vkd3d_symbol *spirv_compiler_emit_io_register(struct spirv_compiler *compiler, + const struct vkd3d_shader_dst_param *dst) + { +@@ -5581,11 +5559,13 @@ static const struct vkd3d_symbol *spirv_compiler_emit_io_register(struct spirv_c + const struct vkd3d_spirv_builtin *builtin; + struct vkd3d_symbol reg_symbol; + SpvStorageClass storage_class; ++ unsigned int array_size; + uint32_t write_mask, id; + struct rb_entry *entry; + +- VKD3D_ASSERT(!reg->idx_count || !reg->idx[0].rel_addr); +- VKD3D_ASSERT(reg->idx_count < 2); ++ VKD3D_ASSERT(reg->idx_count < 1 || !reg->idx[0].rel_addr); ++ VKD3D_ASSERT(reg->idx_count < 2 || !reg->idx[1].rel_addr); ++ VKD3D_ASSERT(reg->idx_count < 3); + + if (reg->type == VKD3DSPR_RASTOUT && reg->idx[0].offset == VSIR_RASTOUT_POINT_SIZE) + { +@@ -5603,7 +5583,8 @@ static const struct vkd3d_symbol *spirv_compiler_emit_io_register(struct spirv_c + if ((entry = rb_get(&compiler->symbol_table, ®_symbol))) + return RB_ENTRY_VALUE(entry, struct vkd3d_symbol, entry); + +- id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, 0); ++ array_size = (reg->idx_count > 1) ? reg->idx[0].offset : 0; ++ id = spirv_compiler_emit_builtin_variable(compiler, builtin, storage_class, array_size); + spirv_compiler_emit_register_execution_mode(compiler, reg->type); + spirv_compiler_emit_register_debug_name(builder, id, reg); + +@@ -5667,11 +5648,8 @@ static void spirv_compiler_emit_input(struct spirv_compiler *compiler, + + array_sizes[0] = signature_element->register_count; + array_sizes[1] = (reg_type == VKD3DSPR_PATCHCONST ? 0 : compiler->input_control_point_count); +- if (array_sizes[0] == 1 && !vsir_sysval_semantic_is_tess_factor(signature_element->sysval_semantic) +- && (!vsir_sysval_semantic_is_clip_cull(signature_element->sysval_semantic) || array_sizes[1])) +- { ++ if (!vsir_signature_element_is_array(signature_element, &compiler->program->normalisation_flags)) + array_sizes[0] = 0; +- } + + write_mask = signature_element->mask; + +@@ -5708,7 +5686,7 @@ static void spirv_compiler_emit_input(struct spirv_compiler *compiler, + * 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)) ++ if (reg_type != VKD3DSPR_INPUT || !vsir_opcode_is_fork_or_join_phase(compiler->phase)) + FIXME("Duplicate input definition found.\n"); + return; + } +@@ -5729,7 +5707,7 @@ static void spirv_compiler_emit_input(struct spirv_compiler *compiler, + if (reg_type == VKD3DSPR_PATCHCONST) + { + vkd3d_spirv_build_op_decorate(builder, input_id, SpvDecorationPatch, NULL, 0); +- location += shader_signature_next_location(&compiler->program->input_signature); ++ location += vsir_signature_next_location(&compiler->program->input_signature); + } + vkd3d_spirv_build_op_decorate1(builder, input_id, SpvDecorationLocation, location); + if (component_idx) +@@ -5803,88 +5781,6 @@ static bool is_dual_source_blending(const struct spirv_compiler *compiler) + return compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL && info && info->dual_source_blending; + } + +-static void calculate_clip_or_cull_distance_mask(const struct signature_element *e, uint32_t *mask) +-{ +- unsigned int write_mask; +- +- if (e->semantic_index >= sizeof(*mask) * CHAR_BIT / VKD3D_VEC4_SIZE) +- { +- FIXME("Invalid semantic index %u for clip/cull distance.\n", e->semantic_index); +- return; +- } +- +- write_mask = e->mask; +- *mask |= (write_mask & VKD3DSP_WRITEMASK_ALL) << (VKD3D_VEC4_SIZE * e->semantic_index); +-} +- +-/* Emits arrayed SPIR-V built-in variables. */ +-static void spirv_compiler_emit_shader_signature_outputs(struct spirv_compiler *compiler) +-{ +- const struct shader_signature *output_signature = &compiler->program->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; +- unsigned int i, count; +- +- for (i = 0; i < output_signature->element_count; ++i) +- { +- const struct signature_element *e = &output_signature->elements[i]; +- +- switch (e->sysval_semantic) +- { +- case VKD3D_SHADER_SV_CLIP_DISTANCE: +- calculate_clip_or_cull_distance_mask(e, &clip_distance_mask); +- break; +- +- case VKD3D_SHADER_SV_CULL_DISTANCE: +- calculate_clip_or_cull_distance_mask(e, &cull_distance_mask); +- break; +- +- default: +- break; +- } +- } +- +- if (clip_distance_mask) +- { +- count = vkd3d_popcount(clip_distance_mask); +- builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SHADER_SV_CLIP_DISTANCE); +- clip_distance_id = spirv_compiler_emit_builtin_variable(compiler, +- builtin, SpvStorageClassOutput, count); +- } +- +- if (cull_distance_mask) +- { +- count = vkd3d_popcount(cull_distance_mask); +- builtin = get_spirv_builtin_for_sysval(compiler, VKD3D_SHADER_SV_CULL_DISTANCE); +- cull_distance_id = spirv_compiler_emit_builtin_variable(compiler, +- builtin, SpvStorageClassOutput, count); +- } +- +- for (i = 0; i < output_signature->element_count; ++i) +- { +- const struct signature_element *e = &output_signature->elements[i]; +- +- switch (e->sysval_semantic) +- { +- case VKD3D_SHADER_SV_CLIP_DISTANCE: +- compiler->output_info[i].id = clip_distance_id; +- compiler->output_info[i].data_type = VSIR_DATA_F32; +- compiler->output_info[i].array_element_mask = clip_distance_mask; +- break; +- +- case VKD3D_SHADER_SV_CULL_DISTANCE: +- compiler->output_info[i].id = cull_distance_id; +- compiler->output_info[i].data_type = VSIR_DATA_F32; +- compiler->output_info[i].array_element_mask = cull_distance_mask; +- break; +- +- default: +- break; +- } +- } +-} +- + static uint32_t spirv_compiler_emit_shader_phase_builtin_variable(struct spirv_compiler *compiler, + const struct vkd3d_spirv_builtin *builtin, const unsigned int *array_sizes, unsigned int size_count) + { +@@ -5902,7 +5798,7 @@ static uint32_t spirv_compiler_emit_shader_phase_builtin_variable(struct spirv_c + return *variable_id; + + id = spirv_compiler_emit_builtin_variable_v(compiler, builtin, SpvStorageClassOutput, array_sizes, size_count); +- if (is_in_fork_or_join_phase(compiler)) ++ if (vsir_opcode_is_fork_or_join_phase(compiler->phase)) + vkd3d_spirv_build_op_decorate(builder, id, SpvDecorationPatch, NULL, 0); + + if (variable_id) +@@ -5940,7 +5836,7 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, + sysval = VKD3D_SHADER_SV_NONE; + array_sizes[0] = signature_element->register_count; + array_sizes[1] = (reg_type == VKD3DSPR_PATCHCONST ? 0 : compiler->output_control_point_count); +- if (array_sizes[0] == 1 && !vsir_sysval_semantic_is_tess_factor(signature_element->sysval_semantic)) ++ if (!vsir_signature_element_is_array(signature_element, &compiler->program->normalisation_flags)) + array_sizes[0] = 0; + + builtin = vkd3d_get_spirv_builtin(compiler, reg_type, sysval); +@@ -5966,8 +5862,7 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, + use_private_variable = true; + + if (!is_patch_constant +- && (get_shader_output_swizzle(compiler, signature_element->register_index) != VKD3D_SHADER_NO_SWIZZLE +- || (compiler->output_info[element_idx].id && compiler->output_info[element_idx].array_element_mask))) ++ && get_shader_output_swizzle(compiler, signature_element->register_index) != VKD3D_SHADER_NO_SWIZZLE) + { + use_private_variable = true; + } +@@ -6005,7 +5900,7 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, + unsigned int location = signature_element->target_location; + + if (is_patch_constant) +- location += shader_signature_next_location(&compiler->program->output_signature); ++ location += vsir_signature_next_location(&compiler->program->output_signature); + else if (compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL + && signature_element->sysval_semantic == VKD3D_SHADER_SV_TARGET) + location = signature_element->semantic_index; +@@ -6066,36 +5961,18 @@ static void spirv_compiler_emit_output(struct spirv_compiler *compiler, + } + } + +-static uint32_t spirv_compiler_get_output_array_index(struct spirv_compiler *compiler, +- const struct signature_element *e) +-{ +- enum vkd3d_shader_sysval_semantic sysval = e->sysval_semantic; +- const struct vkd3d_spirv_builtin *builtin; +- +- builtin = get_spirv_builtin_for_sysval(compiler, sysval); +- +- switch (sysval) +- { +- case VKD3D_SHADER_SV_TESS_FACTOR_LINEDEN: +- case VKD3D_SHADER_SV_TESS_FACTOR_LINEDET: +- return builtin->member_idx; +- default: +- return e->semantic_index; +- } +-} +- + static void spirv_compiler_emit_store_shader_output(struct spirv_compiler *compiler, + const struct shader_signature *signature, const struct signature_element *output, + const struct vkd3d_shader_output_info *output_info, + uint32_t output_index_id, uint32_t val_id, uint32_t write_mask) + { +- uint32_t dst_write_mask, use_mask, uninit_mask, swizzle, mask; + struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; +- uint32_t type_id, zero_id, ptr_type_id, chain_id, object_id; ++ uint32_t dst_write_mask, use_mask, uninit_mask, swizzle; + const struct signature_element *element; +- unsigned int i, index, array_idx; ++ uint32_t type_id, zero_id, ptr_type_id; + enum vsir_data_type data_type; + uint32_t output_id; ++ unsigned int i; + + dst_write_mask = output->mask; + use_mask = output->used_mask; +@@ -6149,31 +6026,8 @@ static void spirv_compiler_emit_store_shader_output(struct spirv_compiler *compi + output_id = vkd3d_spirv_build_op_access_chain1(builder, ptr_type_id, output_id, output_index_id); + } + +- if (!output_info->array_element_mask) +- { +- spirv_compiler_emit_store(compiler, output_id, dst_write_mask, +- data_type, SpvStorageClassOutput, write_mask, val_id); +- return; +- } +- +- type_id = spirv_get_type_id(compiler, data_type, 1); +- ptr_type_id = vkd3d_spirv_get_op_type_pointer(builder, SpvStorageClassOutput, type_id); +- mask = output_info->array_element_mask; +- array_idx = spirv_compiler_get_output_array_index(compiler, output); +- mask &= (1u << (array_idx * VKD3D_VEC4_SIZE)) - 1; +- for (i = 0, index = vkd3d_popcount(mask); i < VKD3D_VEC4_SIZE; ++i) +- { +- if (!(write_mask & (VKD3DSP_WRITEMASK_0 << i))) +- continue; +- +- chain_id = vkd3d_spirv_build_op_access_chain1(builder, +- ptr_type_id, output_id, spirv_compiler_get_constant_uint(compiler, index)); +- object_id = spirv_compiler_emit_swizzle(compiler, val_id, write_mask, +- data_type, VKD3D_SHADER_NO_SWIZZLE, VKD3DSP_WRITEMASK_0 << i); +- spirv_compiler_emit_store(compiler, chain_id, VKD3DSP_WRITEMASK_0, data_type, +- SpvStorageClassOutput, VKD3DSP_WRITEMASK_0 << i, object_id); +- ++index; +- } ++ spirv_compiler_emit_store(compiler, output_id, dst_write_mask, ++ data_type, SpvStorageClassOutput, write_mask, val_id); + } + + static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler *compiler) +@@ -6190,7 +6044,7 @@ static void spirv_compiler_emit_shader_epilogue_function(struct spirv_compiler * + STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(param_type_id)); + STATIC_ASSERT(ARRAY_SIZE(compiler->private_output_variable) == ARRAY_SIZE(compiler->private_output_variable_write_mask)); + +- is_patch_constant = is_in_fork_or_join_phase(compiler); ++ is_patch_constant = vsir_opcode_is_fork_or_join_phase(compiler->phase); + + signature = is_patch_constant ? &compiler->program->patch_constant_signature + : &compiler->program->output_signature; +@@ -6224,7 +6078,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_in_control_point_phase(compiler)) ++ if (vsir_opcode_is_control_point_phase(compiler->phase)) + output_index_id = spirv_compiler_emit_load_invocation_id(compiler); + + for (i = 0; i < signature->element_count; ++i) +@@ -7259,7 +7113,7 @@ static void spirv_compiler_leave_shader_phase(struct spirv_compiler *compiler) + + vkd3d_spirv_build_op_function_end(builder); + +- if (is_in_control_point_phase(compiler)) ++ if (vsir_opcode_is_control_point_phase(compiler->phase)) + { + if (compiler->epilogue_function_id) + { +@@ -7296,8 +7150,8 @@ static void spirv_compiler_enter_shader_phase(struct spirv_compiler *compiler, + compiler->phase = instruction->opcode; + spirv_compiler_emit_shader_phase_name(compiler, function_id, NULL); + +- phase = (instruction->opcode == VSIR_OP_HS_CONTROL_POINT_PHASE) +- ? &compiler->control_point_phase : &compiler->patch_constant_phase; ++ phase = vsir_opcode_is_control_point_phase(instruction->opcode) ++ ? &compiler->control_point_phase : &compiler->patch_constant_phase; + phase->function_id = function_id; + /* The insertion location must be set after the label is emitted. */ + phase->function_location = 0; +@@ -7310,8 +7164,8 @@ static void spirv_compiler_initialise_block(struct spirv_compiler *compiler) + /* Insertion locations must point immediately after the function's initial label. */ + if (compiler->shader_type == VKD3D_SHADER_TYPE_HULL) + { +- struct vkd3d_shader_phase *phase = (compiler->phase == VSIR_OP_HS_CONTROL_POINT_PHASE) +- ? &compiler->control_point_phase : &compiler->patch_constant_phase; ++ struct vkd3d_shader_phase *phase = vsir_opcode_is_control_point_phase(compiler->phase) ++ ? &compiler->control_point_phase : &compiler->patch_constant_phase; + if (!phase->function_location) + phase->function_location = vkd3d_spirv_stream_current_location(&builder->function_stream); + } +@@ -8362,7 +8216,7 @@ static void spirv_compiler_emit_return(struct spirv_compiler *compiler, + spirv_compiler_end_invocation_interlock(compiler); + + if (compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY && (is_in_default_phase(compiler) +- || is_in_control_point_phase(compiler))) ++ || vsir_opcode_is_control_point_phase(compiler->phase))) + spirv_compiler_emit_shader_epilogue_invocation(compiler); + + vkd3d_spirv_build_op_return(builder); +@@ -10967,9 +10821,6 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, + || (program->shader_version.type == VKD3D_SHADER_TYPE_HULL && !spirv_compiler_is_opengl_target(compiler))) + spirv_compiler_emit_tessellator_domain(compiler, program->tess_domain); + +- if (compiler->shader_type != VKD3D_SHADER_TYPE_HULL) +- spirv_compiler_emit_shader_signature_outputs(compiler); +- + it = vsir_program_iterator(&program->instructions); + for (ins = vsir_program_iterator_head(&it); ins && result >= 0; ins = vsir_program_iterator_next(&it)) + { +@@ -11071,8 +10922,9 @@ int spirv_compile(struct vsir_program *program, uint64_t config_flags, + return ret; + + VKD3D_ASSERT(program->normalisation_level == VSIR_NORMALISED_SM6); +- VKD3D_ASSERT(program->has_descriptor_info); +- VKD3D_ASSERT(program->has_no_modifiers); ++ VKD3D_ASSERT(program->normalisation_flags.normalised_clip_cull_arrays); ++ VKD3D_ASSERT(program->normalisation_flags.has_descriptor_info); ++ VKD3D_ASSERT(program->normalisation_flags.has_no_modifiers); + + if (!(spirv_compiler = spirv_compiler_create(program, compile_info, + message_context, config_flags))) +diff --git a/libs/vkd3d/libs/vkd3d-shader/tpf.c b/libs/vkd3d/libs/vkd3d-shader/tpf.c +index 4798a75ce90..3eec61864b4 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/tpf.c ++++ b/libs/vkd3d/libs/vkd3d-shader/tpf.c +@@ -964,11 +964,6 @@ static void shader_sm4_read_dcl_sampler(struct vkd3d_shader_instruction *ins, ui + shader_sm4_read_register_space(priv, &tokens, end, &ins->declaration.sampler.range.space); + } + +-static bool sm4_parser_is_in_fork_or_join_phase(const struct vkd3d_shader_sm4_parser *sm4) +-{ +- return sm4->phase == VSIR_OP_HS_FORK_PHASE || sm4->phase == VSIR_OP_HS_JOIN_PHASE; +-} +- + static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins, uint32_t opcode, + uint32_t opcode_token, const uint32_t *tokens, unsigned int token_count, struct vkd3d_shader_sm4_parser *priv) + { +@@ -997,7 +992,7 @@ static void shader_sm4_read_dcl_index_range(struct vkd3d_shader_instruction *ins + signature = &program->input_signature; + break; + case VKD3DSPR_OUTPUT: +- if (sm4_parser_is_in_fork_or_join_phase(priv)) ++ if (vsir_opcode_is_fork_or_join_phase(priv->phase)) + { + io_masks = priv->patch_constant_register_masks; + ranges = &priv->patch_constant_index_ranges; +@@ -2285,7 +2280,7 @@ static bool register_is_control_point_input(const struct vkd3d_shader_register * + const struct vkd3d_shader_sm4_parser *priv) + { + return reg->type == VKD3DSPR_INCONTROLPOINT || reg->type == VKD3DSPR_OUTCONTROLPOINT +- || (reg->type == VKD3DSPR_INPUT && (priv->phase == VSIR_OP_HS_CONTROL_POINT_PHASE ++ || (reg->type == VKD3DSPR_INPUT && (vsir_opcode_is_control_point_phase(priv->phase) + || priv->program->shader_version.type == VKD3D_SHADER_TYPE_GEOMETRY)); + } + +@@ -2319,8 +2314,8 @@ static bool shader_sm4_validate_input_output_register(struct vkd3d_shader_sm4_pa + masks = priv->input_register_masks; + break; + case VKD3DSPR_OUTPUT: +- masks = sm4_parser_is_in_fork_or_join_phase(priv) ? priv->patch_constant_register_masks +- : priv->output_register_masks; ++ masks = vsir_opcode_is_fork_or_join_phase(priv->phase) ++ ? priv->patch_constant_register_masks : priv->output_register_masks; + break; + case VKD3DSPR_COLOROUT: + case VKD3DSPR_OUTCONTROLPOINT: +@@ -2661,7 +2656,7 @@ static void shader_sm4_read_instruction(struct vkd3d_shader_sm4_parser *sm4, str + if (ins->opcode == VSIR_OP_HS_CONTROL_POINT_PHASE || ins->opcode == VSIR_OP_HS_FORK_PHASE + || ins->opcode == VSIR_OP_HS_JOIN_PHASE) + sm4->phase = ins->opcode; +- sm4->has_control_point_phase |= ins->opcode == VSIR_OP_HS_CONTROL_POINT_PHASE; ++ sm4->has_control_point_phase |= vsir_opcode_is_control_point_phase(ins->opcode); + ins->flags = 0; + ins->coissue = false; + ins->raw = false; +diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c +index 6f0520f19f9..68285be0a49 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c ++++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_main.c +@@ -319,14 +319,21 @@ void vkd3d_string_buffer_release(struct vkd3d_string_buffer_cache *cache, struct + cache->buffers[cache->count++] = buffer; + } + +-void vkd3d_shader_code_from_string_buffer(struct vkd3d_shader_code *code, struct vkd3d_string_buffer *buffer) ++static char *vkd3d_shader_string_from_string_buffer(struct vkd3d_string_buffer *buffer) + { +- code->code = buffer->buffer; +- code->size = buffer->content_size; ++ char *s = buffer->buffer; + + buffer->buffer = NULL; + buffer->buffer_size = 0; + buffer->content_size = 0; ++ ++ return s; ++} ++ ++void vkd3d_shader_code_from_string_buffer(struct vkd3d_shader_code *code, struct vkd3d_string_buffer *buffer) ++{ ++ code->size = buffer->content_size; ++ code->code = vkd3d_shader_string_from_string_buffer(buffer); + } + + void vkd3d_shader_message_context_init(struct vkd3d_shader_message_context *context, +@@ -347,23 +354,15 @@ void vkd3d_shader_message_context_trace_messages_(const struct vkd3d_shader_mess + vkd3d_string_buffer_trace_(&context->messages, function); + } + +-bool vkd3d_shader_message_context_copy_messages(struct vkd3d_shader_message_context *context, char **out) ++void vkd3d_shader_string_from_message_context(char **out, struct vkd3d_shader_message_context *context) + { +- char *messages; +- + if (!out) +- return true; +- +- *out = NULL; +- +- if (!context->messages.content_size) +- return true; ++ return; + +- if (!(messages = vkd3d_malloc(context->messages.content_size + 1))) +- return false; +- memcpy(messages, context->messages.buffer, context->messages.content_size + 1); +- *out = messages; +- return true; ++ if (context->messages.content_size) ++ *out = vkd3d_shader_string_from_string_buffer(&context->messages); ++ else ++ *out = NULL; + } + + void vkd3d_shader_vnote(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location, +@@ -1677,7 +1676,7 @@ static int vsir_program_scan(struct vsir_program *program, const struct vkd3d_sh + add_descriptor_info = true; + } + +- if (program->has_descriptor_info) ++ if (program->normalisation_flags.has_descriptor_info) + add_descriptor_info = false; + + tessellation_info = vkd3d_find_struct(compile_info->next, SCAN_HULL_SHADER_TESSELLATION_INFO); +@@ -1687,7 +1686,7 @@ static int vsir_program_scan(struct vsir_program *program, const struct vkd3d_sh + add_descriptor_info ? &program->descriptors : NULL, combined_sampler_info, message_context); + + if (add_descriptor_info) +- program->has_descriptor_info = true; ++ program->normalisation_flags.has_descriptor_info = true; + + if (TRACE_ON()) + vsir_program_trace(program); +@@ -1772,8 +1771,7 @@ int vkd3d_shader_scan(const struct vkd3d_shader_compile_info *compile_info, char + } + + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + return ret; + } +@@ -1920,8 +1918,7 @@ int vkd3d_shader_compile(const struct vkd3d_shader_compile_info *compile_info, + vkd3d_shader_dump_shader(&dump_data, out->code, out->size, SHADER_DUMP_TYPE_TARGET); + + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + return ret; + } +@@ -2039,9 +2036,7 @@ int vkd3d_shader_parse_input_signature(const struct vkd3d_shader_code *dxbc, + + ret = shader_parse_input_signature(dxbc, &message_context, &shader_signature); + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; +- ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + + if (!vkd3d_shader_signature_from_shader_signature(signature, &shader_signature)) +@@ -2246,8 +2241,7 @@ int vkd3d_shader_preprocess(const struct vkd3d_shader_compile_info *compile_info + vkd3d_shader_dump_shader(&dump_data, out->code, out->size, SHADER_DUMP_TYPE_PREPROC); + + vkd3d_shader_message_context_trace_messages(&message_context); +- if (!vkd3d_shader_message_context_copy_messages(&message_context, messages)) +- ret = VKD3D_ERROR_OUT_OF_MEMORY; ++ vkd3d_shader_string_from_message_context(messages, &message_context); + vkd3d_shader_message_context_cleanup(&message_context); + return ret; + } +diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +index 7e31a77da05..46f62a9e55c 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h ++++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +@@ -632,6 +632,16 @@ enum vkd3d_shader_opcode + + const char *vsir_opcode_get_name(enum vkd3d_shader_opcode op, const char *error); + ++static inline bool vsir_opcode_is_fork_or_join_phase(enum vkd3d_shader_opcode op) ++{ ++ return op == VSIR_OP_HS_FORK_PHASE || op == VSIR_OP_HS_JOIN_PHASE; ++} ++ ++static inline bool vsir_opcode_is_control_point_phase(enum vkd3d_shader_opcode op) ++{ ++ return op == VSIR_OP_HS_CONTROL_POINT_PHASE; ++} ++ + enum vkd3d_shader_register_type + { + VKD3DSPR_TEMP, +@@ -973,6 +983,13 @@ struct vkd3d_shader_version + uint8_t minor; + }; + ++struct vsir_normalisation_flags ++{ ++ bool has_descriptor_info; ++ bool has_no_modifiers; ++ bool normalised_clip_cull_arrays; ++}; ++ + struct vkd3d_shader_immediate_constant_buffer + { + unsigned int register_idx; +@@ -1156,6 +1173,17 @@ enum vkd3d_shader_input_sysval_semantic + + #define SIGNATURE_TARGET_LOCATION_UNUSED (~0u) + ++static inline bool vsir_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 inline bool vsir_sysval_semantic_is_clip_cull(enum vkd3d_shader_sysval_semantic sysval_semantic) ++{ ++ return sysval_semantic == VKD3D_SHADER_SV_CLIP_DISTANCE || sysval_semantic == VKD3D_SHADER_SV_CULL_DISTANCE; ++} ++ + struct signature_element + { + /* sort_index is not a property of the signature element, it is just a +@@ -1179,6 +1207,20 @@ struct signature_element + unsigned int target_location; + }; + ++static inline bool vsir_signature_element_is_array(const struct signature_element *element, ++ const struct vsir_normalisation_flags *flags) ++{ ++ enum vkd3d_shader_sysval_semantic semantic = element->sysval_semantic; ++ ++ if (element->register_count > 1) ++ return true; ++ if (vsir_sysval_semantic_is_tess_factor(semantic)) ++ return true; ++ if (flags->normalised_clip_cull_arrays && vsir_sysval_semantic_is_clip_cull(semantic)) ++ return true; ++ return false; ++} ++ + struct shader_signature + { + struct signature_element *elements; +@@ -1186,21 +1228,11 @@ struct shader_signature + unsigned int element_count; + }; + +-static inline bool vsir_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 inline bool vsir_sysval_semantic_is_clip_cull(enum vkd3d_shader_sysval_semantic sysval_semantic) +-{ +- return sysval_semantic == VKD3D_SHADER_SV_CLIP_DISTANCE || sysval_semantic == VKD3D_SHADER_SV_CULL_DISTANCE; +-} +- + struct signature_element *vsir_signature_find_element_for_reg(const struct shader_signature *signature, + unsigned int reg_idx, unsigned int write_mask); + bool vsir_signature_find_sysval(const struct shader_signature *signature, + enum vkd3d_shader_sysval_semantic sysval, unsigned int semantic_index, unsigned int *element_index); ++unsigned int vsir_signature_next_location(const struct shader_signature *signature); + void shader_signature_cleanup(struct shader_signature *signature); + + struct vsir_features +@@ -1596,7 +1628,6 @@ struct vsir_program + struct shader_signature patch_constant_signature; + + struct vkd3d_shader_scan_descriptor_info1 descriptors; +- bool has_descriptor_info; + size_t descriptors_size; + + unsigned int parameter_count; +@@ -1615,14 +1646,15 @@ struct vsir_program + bool has_fog; + uint8_t diffuse_written_mask; + enum vsir_control_flow_type cf_type; +- enum vsir_normalisation_level normalisation_level; +- bool has_no_modifiers; + enum vkd3d_tessellator_domain tess_domain; + enum vkd3d_shader_tessellator_partitioning tess_partitioning; + enum vkd3d_shader_tessellator_output_primitive tess_output_primitive; + enum vkd3d_primitive_type input_primitive, output_topology; + unsigned int vertices_out_count; + ++ enum vsir_normalisation_level normalisation_level; ++ struct vsir_normalisation_flags normalisation_flags; ++ + uint32_t io_dcls[VKD3D_BITMAP_SIZE(VKD3DSPR_COUNT)]; + + struct vsir_features features; +@@ -1801,7 +1833,6 @@ struct vkd3d_shader_message_context + }; + + void vkd3d_shader_message_context_cleanup(struct vkd3d_shader_message_context *context); +-bool vkd3d_shader_message_context_copy_messages(struct vkd3d_shader_message_context *context, char **out); + void vkd3d_shader_message_context_init(struct vkd3d_shader_message_context *context, + enum vkd3d_shader_log_level log_level); + void vkd3d_shader_message_context_trace_messages_(const struct vkd3d_shader_message_context *context, +@@ -1819,6 +1850,8 @@ void vkd3d_shader_warning(struct vkd3d_shader_message_context *context, const st + void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location, + enum vkd3d_shader_error error, const char *format, va_list args); + ++void vkd3d_shader_string_from_message_context(char **out, struct vkd3d_shader_message_context *context); ++ + uint64_t vkd3d_shader_init_config_flags(void); + void vkd3d_shader_trace_text_(const char *text, size_t size, const char *function); + #define vkd3d_shader_trace_text(text, size) \ +diff --git a/libs/vkd3d/libs/vkd3d/device.c b/libs/vkd3d/libs/vkd3d/device.c +index 6af5e2a5c7d..33628d48609 100644 +--- a/libs/vkd3d/libs/vkd3d/device.c ++++ b/libs/vkd3d/libs/vkd3d/device.c +@@ -109,6 +109,7 @@ static const struct vkd3d_optional_extension_info optional_device_extensions[] = + VK_EXTENSION(EXT_FRAGMENT_SHADER_INTERLOCK, EXT_fragment_shader_interlock), + VK_EXTENSION(EXT_MUTABLE_DESCRIPTOR_TYPE, EXT_mutable_descriptor_type), + VK_EXTENSION(EXT_ROBUSTNESS_2, EXT_robustness2), ++ VK_EXTENSION(EXT_SAMPLER_FILTER_MINMAX, EXT_sampler_filter_minmax), + VK_EXTENSION(EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION, EXT_shader_demote_to_helper_invocation), + VK_EXTENSION(EXT_SHADER_STENCIL_EXPORT, EXT_shader_stencil_export), + VK_EXTENSION(EXT_SHADER_VIEWPORT_INDEX_LAYER, EXT_shader_viewport_index_layer), +@@ -835,6 +836,7 @@ struct vkd3d_physical_device_info + /* properties */ + VkPhysicalDeviceDescriptorIndexingPropertiesEXT descriptor_indexing_properties; + VkPhysicalDeviceMaintenance3Properties maintenance3_properties; ++ VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT filter_minmax_properties; + VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties; + VkPhysicalDeviceTransformFeedbackPropertiesEXT xfb_properties; + VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_divisor_properties; +@@ -900,6 +902,8 @@ static void vkd3d_chain_physical_device_info_structures(struct vkd3d_physical_de + vk_prepend_struct(&info->properties2, &info->maintenance3_properties); + if (vulkan_info->EXT_descriptor_indexing) + vk_prepend_struct(&info->properties2, &info->descriptor_indexing_properties); ++ if (vulkan_info->EXT_sampler_filter_minmax) ++ vk_prepend_struct(&info->properties2, &info->filter_minmax_properties); + if (vulkan_info->EXT_texel_buffer_alignment) + vk_prepend_struct(&info->properties2, &info->texel_buffer_alignment_properties); + if (vulkan_info->EXT_transform_feedback) +@@ -936,6 +940,7 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i + info->properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + info->maintenance3_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES; + info->descriptor_indexing_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT; ++ info->filter_minmax_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT; + info->texel_buffer_alignment_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT; + info->xfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; + info->vertex_divisor_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT; +@@ -1017,6 +1022,7 @@ static void vkd3d_trace_physical_device_limits(const struct vkd3d_physical_devic + const VkPhysicalDeviceLimits *limits = &info->properties2.properties.limits; + const VkPhysicalDeviceDescriptorIndexingPropertiesEXT *descriptor_indexing; + const VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT *buffer_alignment; ++ const VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT *minmax; + const VkPhysicalDeviceMaintenance3Properties *maintenance3; + const VkPhysicalDeviceTransformFeedbackPropertiesEXT *xfb; + +@@ -1196,6 +1202,11 @@ static void vkd3d_trace_physical_device_limits(const struct vkd3d_physical_devic + TRACE(" maxPerSetDescriptors: %u.\n", maintenance3->maxPerSetDescriptors); + TRACE(" maxMemoryAllocationSize: %#"PRIx64".\n", maintenance3->maxMemoryAllocationSize); + ++ minmax = &info->filter_minmax_properties; ++ TRACE(" VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT:\n"); ++ TRACE(" filterMinmaxSingleComponentFormats: %#x.\n", minmax->filterMinmaxSingleComponentFormats); ++ TRACE(" filterMinmaxImageComponentMapping: %#x.\n", minmax->filterMinmaxImageComponentMapping); ++ + buffer_alignment = &info->texel_buffer_alignment_properties; + TRACE(" VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT:\n"); + TRACE(" storageTexelBufferOffsetAlignmentBytes: %#"PRIx64".\n", +@@ -1866,6 +1877,12 @@ static HRESULT vkd3d_init_device_caps(struct d3d12_device *device, + + physical_device_info->formats4444_features.formatA4B4G4R4 = VK_FALSE; + ++ if (!vulkan_info->EXT_sampler_filter_minmax) ++ WARN("Sampler min/max reduction filtering is not supported.\n"); ++ else if (!physical_device_info->filter_minmax_properties.filterMinmaxSingleComponentFormats ++ || !physical_device_info->filter_minmax_properties.filterMinmaxImageComponentMapping) ++ WARN("Sampler min/max reduction filtering is only partially supported."); ++ + vulkan_info->texel_buffer_alignment_properties = physical_device_info->texel_buffer_alignment_properties; + + if (get_spec_version(vk_extensions, vk_extension_count, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) >= 3) +diff --git a/libs/vkd3d/libs/vkd3d/resource.c b/libs/vkd3d/libs/vkd3d/resource.c +index 7946445ad07..f1491cbc2b6 100644 +--- a/libs/vkd3d/libs/vkd3d/resource.c ++++ b/libs/vkd3d/libs/vkd3d/resource.c +@@ -3661,6 +3661,24 @@ bool vkd3d_create_raw_buffer_view(struct d3d12_device *device, + } + + /* samplers */ ++ ++static VkSamplerReductionModeEXT vk_reduction_mode_from_d3d12(D3D12_FILTER_REDUCTION_TYPE mode) ++{ ++ switch (mode) ++ { ++ case D3D12_FILTER_REDUCTION_TYPE_STANDARD: ++ case D3D12_FILTER_REDUCTION_TYPE_COMPARISON: ++ return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE; ++ case D3D12_FILTER_REDUCTION_TYPE_MINIMUM: ++ return VK_SAMPLER_REDUCTION_MODE_MIN; ++ case D3D12_FILTER_REDUCTION_TYPE_MAXIMUM: ++ return VK_SAMPLER_REDUCTION_MODE_MAX; ++ default: ++ FIXME("Unhandled reduction mode %#x.\n", mode); ++ return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE; ++ } ++} ++ + static VkFilter vk_filter_from_d3d12(D3D12_FILTER_TYPE type) + { + switch (type) +@@ -3734,16 +3752,13 @@ static VkResult d3d12_create_sampler(struct d3d12_device *device, D3D12_FILTER f + D3D12_COMPARISON_FUNC comparison_func, D3D12_STATIC_BORDER_COLOR border_colour, + float min_lod, float max_lod, VkSampler *vk_sampler) + { ++ VkSamplerReductionModeCreateInfoEXT reduction_desc; + const struct vkd3d_vk_device_procs *vk_procs; + struct VkSamplerCreateInfo sampler_desc; + VkResult vr; + + vk_procs = &device->vk_procs; + +- if (D3D12_DECODE_FILTER_REDUCTION(filter) == D3D12_FILTER_REDUCTION_TYPE_MINIMUM +- || D3D12_DECODE_FILTER_REDUCTION(filter) == D3D12_FILTER_REDUCTION_TYPE_MAXIMUM) +- FIXME("Min/max reduction mode not supported.\n"); +- + sampler_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_desc.pNext = NULL; + sampler_desc.flags = 0; +@@ -3767,6 +3782,21 @@ static VkResult d3d12_create_sampler(struct d3d12_device *device, D3D12_FILTER f + || address_w == D3D12_TEXTURE_ADDRESS_MODE_BORDER) + sampler_desc.borderColor = vk_border_colour_from_d3d12(border_colour); + ++ reduction_desc.reductionMode = vk_reduction_mode_from_d3d12(D3D12_DECODE_FILTER_REDUCTION(filter)); ++ if (reduction_desc.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE) ++ { ++ if (device->vk_info.EXT_sampler_filter_minmax) ++ { ++ reduction_desc.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT; ++ reduction_desc.pNext = NULL; ++ vk_prepend_struct(&sampler_desc, &reduction_desc); ++ } ++ else ++ { ++ FIXME("Sampler min/max reduction filtering is not supported by the device.\n"); ++ } ++ } ++ + if ((vr = VK_CALL(vkCreateSampler(device->vk_device, &sampler_desc, NULL, vk_sampler))) < 0) + WARN("Failed to create Vulkan sampler, vr %d.\n", vr); + +diff --git a/libs/vkd3d/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/libs/vkd3d/vkd3d_private.h +index b80a206294a..0e3d735e332 100644 +--- a/libs/vkd3d/libs/vkd3d/vkd3d_private.h ++++ b/libs/vkd3d/libs/vkd3d/vkd3d_private.h +@@ -144,6 +144,7 @@ struct vkd3d_vulkan_info + bool EXT_fragment_shader_interlock; + bool EXT_mutable_descriptor_type; + bool EXT_robustness2; ++ bool EXT_sampler_filter_minmax; + bool EXT_shader_demote_to_helper_invocation; + bool EXT_shader_stencil_export; + bool EXT_shader_viewport_index_layer; +-- +2.51.0 + diff --git a/patches/vkd3d-latest/0003-Updated-vkd3d-to-fdfb74b20b08144e144299bc0b7b20b9542.patch b/patches/vkd3d-latest/0003-Updated-vkd3d-to-fdfb74b20b08144e144299bc0b7b20b9542.patch new file mode 100644 index 00000000..c5960785 --- /dev/null +++ b/patches/vkd3d-latest/0003-Updated-vkd3d-to-fdfb74b20b08144e144299bc0b7b20b9542.patch @@ -0,0 +1,120 @@ +From cf607d9c2b6071769acdad862336e621b332ecb0 Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Fri, 28 Nov 2025 08:54:08 +1100 +Subject: [PATCH] Updated vkd3d to fdfb74b20b08144e144299bc0b7b20b95421c6c8. + +--- + libs/vkd3d/libs/vkd3d-shader/ir.c | 60 +++++++++++++++++++ + .../libs/vkd3d-shader/vkd3d_shader_private.h | 18 ++++++ + 2 files changed, 78 insertions(+) + +diff --git a/libs/vkd3d/libs/vkd3d-shader/ir.c b/libs/vkd3d/libs/vkd3d-shader/ir.c +index 1a0c9d83306..3548e748c18 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/ir.c ++++ b/libs/vkd3d/libs/vkd3d-shader/ir.c +@@ -1323,6 +1323,62 @@ static enum vkd3d_result vsir_program_lower_ifc(struct vsir_program *program, + return VKD3D_OK; + } + ++static enum vkd3d_result vsir_program_lower_nrm(struct vsir_program *program, struct vsir_program_iterator *nrm) ++{ ++ struct vkd3d_shader_instruction *ins = vsir_program_iterator_current(nrm); ++ const struct vkd3d_shader_location location = ins->location; ++ const struct vkd3d_shader_src_param *src = ins->src; ++ const struct vkd3d_shader_dst_param *dst = ins->dst; ++ unsigned int dot_id, rsq_id, mul_id; ++ struct vsir_program_iterator it; ++ ++ /* nrm DST, SRC ++ * -> ++ * dp3 srDOT, SRC, SRC ++ * rsq srRSQ, srDOT ++ * mul srMUL, srRSQ, SRC ++ * movc DST, srDOT, srMUL, srDOT */ ++ ++ if (!(ins = vsir_program_iterator_insert_before(nrm, &it, 3))) ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_DP3, 1, 2)) ++ goto fail; ++ dot_id = program->ssa_count++; ++ dst_param_init_ssa(&ins->dst[0], dot_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR); ++ ins->src[0] = src[0]; ++ ins->src[1] = src[0]; ++ ++ ins = vsir_program_iterator_next(&it); ++ if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_RSQ, 1, 1)) ++ goto fail; ++ rsq_id = program->ssa_count++; ++ dst_param_init_ssa(&ins->dst[0], rsq_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR); ++ src_param_init_ssa(&ins->src[0], dot_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR); ++ ++ ins = vsir_program_iterator_next(&it); ++ if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_MUL, 1, 2)) ++ goto fail; ++ mul_id = program->ssa_count++; ++ dst_param_init_ssa(&ins->dst[0], mul_id, src[0].reg.data_type, dst[0].reg.dimension); ++ src_param_init_ssa(&ins->src[0], rsq_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR); ++ ins->src[1] = src[0]; ++ ++ ins = vsir_program_iterator_next(&it); ++ if (!vsir_instruction_init_with_params(program, ins, &location, VSIR_OP_MOVC, 1, 3)) ++ goto fail; ++ ins->dst[0] = dst[0]; ++ src_param_init_ssa(&ins->src[0], dot_id, VSIR_DATA_U32, VSIR_DIMENSION_SCALAR); ++ src_param_init_ssa(&ins->src[1], mul_id, src[0].reg.data_type, dst[0].reg.dimension); ++ src_param_init_ssa(&ins->src[2], dot_id, src[0].reg.data_type, VSIR_DIMENSION_SCALAR); ++ ++ return VKD3D_OK; ++ ++fail: ++ vsir_program_iterator_nop_range(&it, nrm, &location); ++ ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++} ++ + static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program, + struct vsir_program_iterator *it, unsigned int *tmp_idx) + { +@@ -2364,6 +2420,10 @@ static enum vkd3d_result vsir_program_lower_d3dbc_instructions(struct vsir_progr + ret = vsir_program_lower_ifc(program, &it, &tmp_idx, message_context); + break; + ++ case VSIR_OP_NRM: ++ ret = vsir_program_lower_nrm(program, &it); ++ break; ++ + case VSIR_OP_SINCOS: + ret = vsir_program_lower_sm1_sincos(program, &it); + break; +diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +index 46f62a9e55c..33004dc62d9 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h ++++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +@@ -1572,6 +1572,24 @@ static inline struct vkd3d_shader_instruction *vsir_program_iterator_insert_befo + return vsir_program_iterator_current(it); + } + ++static inline void vsir_program_iterator_nop_range(const struct vsir_program_iterator *first, ++ const struct vsir_program_iterator *last, const struct vkd3d_shader_location *location) ++{ ++ const struct vkd3d_shader_instruction_array *array = first->array; ++ size_t first_idx = first->idx; ++ size_t last_idx = last->idx; ++ size_t idx; ++ ++ VKD3D_ASSERT(last->array == array); ++ VKD3D_ASSERT(last_idx < array->count); ++ VKD3D_ASSERT(first_idx <= last_idx); ++ ++ for (idx = first_idx; idx <= last_idx; ++idx) ++ { ++ vsir_instruction_init(&array->elements[idx], location, VSIR_OP_NOP); ++ } ++} ++ + enum vkd3d_shader_config_flags + { + VKD3D_SHADER_CONFIG_FLAG_FORCE_VALIDATION = 0x00000001, +-- +2.51.0 +