From 321fda9c260b772e652be43f508c7bfdd1965482 Mon Sep 17 00:00:00 2001 From: Francisco Casas Date: Mon, 3 Feb 2025 19:28:25 -0300 Subject: [PATCH] vkd3d-shader/hlsl: Only use the temp copy for variables that are written. This can save a significant amount of temp registers because it allows to avoid referencing the temp (and having to store it) when not needed. For instance, this patch lowers the number of required temps for the following ps_2_0 shader from 24 to 19: int i; float3x3 mats[4]; float4 main() : sv_target { return mul(mats[i], float3(1, 2, 3)).xyzz; } Also, it is needed for SM1 vertex shader relative addressing since non-constant loads are required to be directly on the uniform ('c' registers) instead of the temp, and non-constant loads cannot be transformed by copy propagation. --- libs/vkd3d-shader/hlsl.h | 3 ++ libs/vkd3d-shader/hlsl_codegen.c | 67 +++++++++++++++++++------------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 20bdc872..8e58f20a 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -482,6 +482,9 @@ struct hlsl_ir_var union hlsl_constant_value_component number; } *default_values; + /* Pointer to the temp copy of the variable, in case it is uniform. */ + struct hlsl_ir_var *temp_copy; + /* A dynamic array containing the state block on the variable's declaration, if any. * An array variable may contain multiple state blocks. * A technique pass will always contain one. diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 1725d6be..ea81d81a 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -203,41 +203,34 @@ static bool clean_constant_deref_offset_srcs(struct hlsl_ctx *ctx, struct hlsl_d } -/* Split uniforms into two variables representing the constant and temp - * registers, and copy the former to the latter, so that writes to uniforms - * work. */ -static void prepend_uniform_copy(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_var *temp) +/* For a uniform variable, create a temp copy of it so, in case a value is + * stored to the uniform at some point the shader, all derefs can be diverted + * to this temp copy instead. + * Also, promote the uniform to an extern var. */ +static void prepend_uniform_copy(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_var *uniform) { - struct hlsl_ir_var *uniform; struct hlsl_ir_node *store; struct hlsl_ir_load *load; + struct hlsl_ir_var *temp; char *new_name; - /* Use the synthetic name for the temp, rather than the uniform, so that we - * can write the uniform name into the shader reflection data. */ - - if (!(uniform = hlsl_new_var(ctx, temp->name, temp->data_type, - &temp->loc, NULL, temp->storage_modifiers, &temp->reg_reservation))) - return; - list_add_before(&temp->scope_entry, &uniform->scope_entry); - list_add_tail(&ctx->extern_vars, &uniform->extern_entry); uniform->is_uniform = 1; - uniform->is_param = temp->is_param; - uniform->buffer = temp->buffer; - if (temp->default_values) - { - /* Transfer default values from the temp to the uniform. */ - VKD3D_ASSERT(!uniform->default_values); - VKD3D_ASSERT(hlsl_type_component_count(temp->data_type) == hlsl_type_component_count(uniform->data_type)); - uniform->default_values = temp->default_values; - temp->default_values = NULL; - } + list_add_tail(&ctx->extern_vars, &uniform->extern_entry); - if (!(new_name = hlsl_sprintf_alloc(ctx, "", temp->name))) + if (!(new_name = hlsl_sprintf_alloc(ctx, "", uniform->name))) return; - temp->name = new_name; - if (!(load = hlsl_new_var_load(ctx, uniform, &temp->loc))) + if (!(temp = hlsl_new_var(ctx, new_name, uniform->data_type, + &uniform->loc, NULL, uniform->storage_modifiers, NULL))) + { + vkd3d_free(new_name); + return; + } + list_add_before(&uniform->scope_entry, &temp->scope_entry); + + uniform->temp_copy = temp; + + if (!(load = hlsl_new_var_load(ctx, uniform, &uniform->loc))) return; list_add_head(&block->instrs, &load->node.entry); @@ -246,6 +239,25 @@ static void prepend_uniform_copy(struct hlsl_ctx *ctx, struct hlsl_block *block, list_add_after(&load->node.entry, &store->entry); } +/* If a uniform is written to at some point in the shader, all dereferences + * must point to the temp copy instead, which is what this pass does. */ +static bool divert_written_uniform_derefs_to_temp(struct hlsl_ctx *ctx, struct hlsl_deref *deref, + struct hlsl_ir_node *instr) +{ + if (!deref->var->is_uniform || !deref->var->first_write) + return false; + + /* Skip derefs from instructions before first write so copies from the + * uniform to the temp are unaffected. */ + if (instr->index < deref->var->first_write) + return false; + + VKD3D_ASSERT(deref->var->temp_copy); + + deref->var = deref->var->temp_copy; + return true; +} + static void validate_field_semantic(struct hlsl_ctx *ctx, struct hlsl_struct_field *field) { if (!field->semantic.name && hlsl_is_numeric_type(hlsl_get_multiarray_element_type(field->type)) @@ -12509,6 +12521,9 @@ static void process_entry_function(struct hlsl_ctx *ctx, lower_ir(ctx, lower_casts_to_bool, body); lower_ir(ctx, lower_int_dot, body); + compute_liveness(ctx, entry_func); + transform_derefs(ctx, divert_written_uniform_derefs_to_temp, &entry_func->body); + if (hlsl_version_lt(ctx, 4, 0)) hlsl_transform_ir(ctx, lower_separate_samples, body, NULL);