From 8a17a5a08bdf78f926b536fd7e29f06c87902961 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani <gmascellani@codeweavers.com> Date: Thu, 11 Apr 2024 12:19:34 +0200 Subject: [PATCH 1/3] vkd3d-shader/ir: Introduce vsir_program_lower_instructions(). It is meant as generic pass to host all program changes to single instructions that do not require keeping a global state, intstead of having to loop through the whole program many times. --- libs/vkd3d-shader/ir.c | 138 +++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 60 deletions(-) diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index a8b92a1e12..56ec26afa3 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -94,86 +94,104 @@ static bool vsir_instruction_init_with_params(struct vsir_program *program, return true; } -static enum vkd3d_result vsir_program_lower_texkills(struct vsir_program *program) +static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program, + struct vkd3d_shader_instruction *texkill, unsigned int *tmp_idx) { + const unsigned int components_read = 3 + (program->shader_version.major >= 2); struct vkd3d_shader_instruction_array *instructions = &program->instructions; - struct vkd3d_shader_instruction *texkill_ins, *ins; - unsigned int components_read = 3 + (program->shader_version.major >= 2); - unsigned int tmp_idx = ~0u; - unsigned int i, k; + size_t pos = texkill - instructions->elements; + struct vkd3d_shader_instruction *ins; + unsigned int j; - for (i = 0; i < instructions->count; ++i) - { - texkill_ins = &instructions->elements[i]; + if (!shader_instruction_array_insert_at(instructions, pos + 1, components_read + 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; - if (texkill_ins->handler_idx != VKD3DSIH_TEXKILL) - continue; + if (*tmp_idx == ~0u) + *tmp_idx = program->temp_count++; - if (!shader_instruction_array_insert_at(instructions, i + 1, components_read + 1)) - return VKD3D_ERROR_OUT_OF_MEMORY; + /* tmp = ins->dst[0] < 0 */ + + ins = &instructions->elements[pos + 1]; + if (!vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_LTO, 1, 2)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); + ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->dst[0].reg.idx[0].offset = *tmp_idx; + ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL; - if (tmp_idx == ~0u) - tmp_idx = program->temp_count++; + ins->src[0].reg = texkill->dst[0].reg; + ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE; + vsir_register_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VKD3D_DATA_FLOAT, 0); + ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[1].reg.u.immconst_f32[0] = 0.0f; + ins->src[1].reg.u.immconst_f32[1] = 0.0f; + ins->src[1].reg.u.immconst_f32[2] = 0.0f; + ins->src[1].reg.u.immconst_f32[3] = 0.0f; - /* tmp = ins->dst[0] < 0 */ + /* tmp.x = tmp.x || tmp.y */ + /* tmp.x = tmp.x || tmp.z */ + /* tmp.x = tmp.x || tmp.w, if sm >= 2.0 */ - ins = &instructions->elements[i + 1]; - if (!vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_LTO, 1, 2)) + for (j = 1; j < components_read; ++j) + { + ins = &instructions->elements[pos + 1 + j]; + if (!(vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_OR, 1, 2))) return VKD3D_ERROR_OUT_OF_MEMORY; vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; - ins->dst[0].reg.idx[0].offset = tmp_idx; - ins->dst[0].write_mask = VKD3DSP_WRITEMASK_ALL; + ins->dst[0].reg.idx[0].offset = *tmp_idx; + ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0; - ins->src[0].reg = texkill_ins->dst[0].reg; - ins->src[0].swizzle = VKD3D_SHADER_NO_SWIZZLE; - vsir_register_init(&ins->src[1].reg, VKD3DSPR_IMMCONST, VKD3D_DATA_FLOAT, 0); + vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[0].reg.idx[0].offset = *tmp_idx; + ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); + vsir_register_init(&ins->src[1].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; - ins->src[1].reg.u.immconst_f32[0] = 0.0f; - ins->src[1].reg.u.immconst_f32[1] = 0.0f; - ins->src[1].reg.u.immconst_f32[2] = 0.0f; - ins->src[1].reg.u.immconst_f32[3] = 0.0f; + ins->src[1].reg.idx[0].offset = *tmp_idx; + ins->src[1].swizzle = vkd3d_shader_create_swizzle(j, j, j, j); + } - /* tmp.x = tmp.x || tmp.y */ - /* tmp.x = tmp.x || tmp.z */ - /* tmp.x = tmp.x || tmp.w, if sm >= 2.0 */ + /* discard_nz tmp.x */ - for (k = 1; k < components_read; ++k) - { - ins = &instructions->elements[i + 1 + k]; - if (!(vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_OR, 1, 2))) - return VKD3D_ERROR_OUT_OF_MEMORY; + ins = &instructions->elements[pos + 1 + components_read]; + if (!(vsir_instruction_init_with_params(program, ins, &texkill->location, VKD3DSIH_DISCARD, 0, 1))) + return VKD3D_ERROR_OUT_OF_MEMORY; + ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ; - vsir_register_init(&ins->dst[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); - ins->dst[0].reg.dimension = VSIR_DIMENSION_VEC4; - ins->dst[0].reg.idx[0].offset = tmp_idx; - ins->dst[0].write_mask = VKD3DSP_WRITEMASK_0; - - vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); - ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; - ins->src[0].reg.idx[0].offset = tmp_idx; - ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); - vsir_register_init(&ins->src[1].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); - ins->src[1].reg.dimension = VSIR_DIMENSION_VEC4; - ins->src[1].reg.idx[0].offset = tmp_idx; - ins->src[1].swizzle = vkd3d_shader_create_swizzle(k, k, k, k); - } + vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); + ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; + ins->src[0].reg.idx[0].offset = *tmp_idx; + ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); - /* discard_nz tmp.x */ + /* Make the original instruction no-op */ + vkd3d_shader_instruction_make_nop(texkill); - ins = &instructions->elements[i + 1 + components_read]; - if (!(vsir_instruction_init_with_params(program, ins, &texkill_ins->location, VKD3DSIH_DISCARD, 0, 1))) - return VKD3D_ERROR_OUT_OF_MEMORY; - ins->flags = VKD3D_SHADER_CONDITIONAL_OP_NZ; + return VKD3D_OK; +} - vsir_register_init(&ins->src[0].reg, VKD3DSPR_TEMP, VKD3D_DATA_UINT, 1); - ins->src[0].reg.dimension = VSIR_DIMENSION_VEC4; - ins->src[0].reg.idx[0].offset = tmp_idx; - ins->src[0].swizzle = VKD3D_SHADER_SWIZZLE(X, X, X, X); +static enum vkd3d_result vsir_program_lower_instructions(struct vsir_program *program) +{ + struct vkd3d_shader_instruction_array *instructions = &program->instructions; + unsigned int tmp_idx = ~0u, i; + enum vkd3d_result ret; - /* Make the original instruction no-op */ - vkd3d_shader_instruction_make_nop(texkill_ins); + for (i = 0; i < instructions->count; ++i) + { + struct vkd3d_shader_instruction *ins = &instructions->elements[i]; + + switch (ins->handler_idx) + { + case VKD3DSIH_TEXKILL: + if ((ret = vsir_program_lower_texkill(program, ins, &tmp_idx)) < 0) + return ret; + break; + + default: + break; + } } return VKD3D_OK; @@ -5751,7 +5769,7 @@ enum vkd3d_result vsir_program_normalise(struct vsir_program *program, uint64_t remove_dcl_temps(program); - if ((result = vsir_program_lower_texkills(program)) < 0) + if ((result = vsir_program_lower_instructions(program)) < 0) return result; if (program->shader_version.major >= 6) -- GitLab From 9112a5be58f7c1a526f10f00c50f9a9768604f0a Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani <gmascellani@codeweavers.com> Date: Thu, 11 Apr 2024 12:24:23 +0200 Subject: [PATCH 2/3] vkd3d-shader/ir: Move DCL_TEMPS removal to the generic instruction lowering pass. --- libs/vkd3d-shader/ir.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index 56ec26afa3..d38ec1df45 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -56,19 +56,6 @@ static void vkd3d_shader_instruction_make_nop(struct vkd3d_shader_instruction *i vsir_instruction_init(ins, &location, VKD3DSIH_NOP); } -static void remove_dcl_temps(struct vsir_program *program) -{ - unsigned int i; - - for (i = 0; i < program->instructions.count; ++i) - { - struct vkd3d_shader_instruction *ins = &program->instructions.elements[i]; - - if (ins->handler_idx == VKD3DSIH_DCL_TEMPS) - vkd3d_shader_instruction_make_nop(ins); - } -} - static bool vsir_instruction_init_with_params(struct vsir_program *program, struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, enum vkd3d_shader_opcode handler_idx, unsigned int dst_count, unsigned int src_count) @@ -189,6 +176,10 @@ static enum vkd3d_result vsir_program_lower_instructions(struct vsir_program *pr return ret; break; + case VKD3DSIH_DCL_TEMPS: + vkd3d_shader_instruction_make_nop(ins); + break; + default: break; } @@ -5767,8 +5758,6 @@ enum vkd3d_result vsir_program_normalise(struct vsir_program *program, uint64_t { enum vkd3d_result result = VKD3D_OK; - remove_dcl_temps(program); - if ((result = vsir_program_lower_instructions(program)) < 0) return result; -- GitLab From 23db06692213273bd488e1ad1520444fd4d7845d Mon Sep 17 00:00:00 2001 From: Conor McCarthy <cmccarthy@codeweavers.com> Date: Thu, 11 Apr 2024 12:38:24 +0200 Subject: [PATCH 3/3] vkd3d-shader/ir: Implement MAD in two operations if flagged as precise. With some changes by Giovanni Mascellani. --- libs/vkd3d-shader/ir.c | 56 +++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 25 +++++++++ .../hlsl/arithmetic-float-uniform.shader_test | 2 +- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index d38ec1df45..610d907d98 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -17,6 +17,7 @@ */ #include "vkd3d_shader_private.h" +#include "vkd3d_types.h" bool vsir_program_init(struct vsir_program *program, const struct vkd3d_shader_version *version, unsigned int reserve) { @@ -159,6 +160,56 @@ static enum vkd3d_result vsir_program_lower_texkill(struct vsir_program *program return VKD3D_OK; } +/* The Shader Model 5 Assembly documentation states: "If components of a mad + * instruction are tagged as precise, the hardware must execute a mad instruction + * or the exact equivalent, and it cannot split it into a multiply followed by an add." + * But DXIL.rst states the opposite: "Floating point multiply & add. This operation is + * not fused for "precise" operations." + * Windows drivers seem to conform with the latter, for SM 4-5 and SM 6. */ +static enum vkd3d_result vsir_program_lower_precise_mad(struct vsir_program *program, + struct vkd3d_shader_instruction *mad, unsigned int *tmp_idx) +{ + struct vkd3d_shader_instruction_array *instructions = &program->instructions; + struct vkd3d_shader_instruction *mul_ins, *add_ins; + size_t pos = mad - instructions->elements; + struct vkd3d_shader_dst_param *mul_dst; + + if (!(mad->flags & VKD3DSI_PRECISE_XYZW)) + return VKD3D_OK; + + if (!shader_instruction_array_insert_at(instructions, pos + 1, 1)) + return VKD3D_ERROR_OUT_OF_MEMORY; + + if (*tmp_idx == ~0u) + *tmp_idx = program->temp_count++; + + mul_ins = &instructions->elements[pos]; + add_ins = &instructions->elements[pos + 1]; + + mul_ins->handler_idx = VKD3DSIH_MUL; + mul_ins->src_count = 2; + + if (!(vsir_instruction_init_with_params(program, add_ins, &mul_ins->location, VKD3DSIH_ADD, 1, 2))) + return VKD3D_ERROR_OUT_OF_MEMORY; + + add_ins->flags = mul_ins->flags & VKD3DSI_PRECISE_XYZW; + + mul_dst = mul_ins->dst; + *add_ins->dst = *mul_dst; + + mul_dst->modifiers = 0; + vsir_register_init(&mul_dst->reg, VKD3DSPR_TEMP, mul_ins->src[0].reg.data_type, 1); + mul_dst->reg.dimension = add_ins->dst->reg.dimension; + mul_dst->reg.idx[0].offset = *tmp_idx; + + add_ins->src[0].reg = mul_dst->reg; + add_ins->src[0].swizzle = vsir_swizzle_from_writemask(mul_dst->write_mask); + add_ins->src[0].modifiers = 0; + add_ins->src[1] = mul_ins->src[2]; + + return VKD3D_OK; +} + static enum vkd3d_result vsir_program_lower_instructions(struct vsir_program *program) { struct vkd3d_shader_instruction_array *instructions = &program->instructions; @@ -176,6 +227,11 @@ static enum vkd3d_result vsir_program_lower_instructions(struct vsir_program *pr return ret; break; + case VKD3DSIH_MAD: + if ((ret = vsir_program_lower_precise_mad(program, ins, &tmp_idx)) < 0) + return ret; + break; + case VKD3DSIH_DCL_TEMPS: vkd3d_shader_instruction_make_nop(ins); break; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index b07a7bff75..f401fd7a3f 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1761,6 +1761,31 @@ static inline unsigned int vkd3d_compact_swizzle(uint32_t swizzle, uint32_t writ return compacted_swizzle; } +static inline uint32_t vsir_swizzle_from_writemask(unsigned int writemask) +{ + static const unsigned int swizzles[16] = + { + 0, + VKD3D_SHADER_SWIZZLE(X, X, X, X), + VKD3D_SHADER_SWIZZLE(Y, Y, Y, Y), + VKD3D_SHADER_SWIZZLE(X, Y, X, X), + VKD3D_SHADER_SWIZZLE(Z, Z, Z, Z), + VKD3D_SHADER_SWIZZLE(X, Z, X, X), + VKD3D_SHADER_SWIZZLE(Y, Z, X, X), + VKD3D_SHADER_SWIZZLE(X, Y, Z, X), + VKD3D_SHADER_SWIZZLE(W, W, W, W), + VKD3D_SHADER_SWIZZLE(X, W, X, X), + VKD3D_SHADER_SWIZZLE(Y, W, X, X), + VKD3D_SHADER_SWIZZLE(X, Y, W, X), + VKD3D_SHADER_SWIZZLE(Z, W, X, X), + VKD3D_SHADER_SWIZZLE(X, Z, W, X), + VKD3D_SHADER_SWIZZLE(Y, Z, W, X), + VKD3D_SHADER_SWIZZLE(X, Y, Z, W), + }; + + return swizzles[writemask & 0xf]; +} + struct vkd3d_struct { enum vkd3d_shader_structure_type type; diff --git a/tests/hlsl/arithmetic-float-uniform.shader_test b/tests/hlsl/arithmetic-float-uniform.shader_test index 61957f2bbb..a49d3f00a4 100644 --- a/tests/hlsl/arithmetic-float-uniform.shader_test +++ b/tests/hlsl/arithmetic-float-uniform.shader_test @@ -121,7 +121,7 @@ uniform 0 float4 1.00000007 -42.1 4.0 45.0 uniform 4 float4 1.625 -5.0 4.125 5.0 uniform 8 float4 1.00000007 -1.0 0.5 -0.5 todo(sm<6) draw quad -todo probe all rgba (2.62500048, 209.5, 17.0, 224.5) +probe all rgba (2.62500048, 209.5, 17.0, 224.5) [require] shader model >= 5.0 -- GitLab