I wanted to use ._xy element access to make it shorter, but that attempt was crushed by the reality - such indexing apparently compiles but does not produce correct element access loads. I'm going to update once this is fixed.
-- v10: vkd3d-shader/hlsl: Add determinant() function.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- Makefile.am | 1 + libs/vkd3d-shader/hlsl.y | 89 ++++++++++++++++++++++ tests/hlsl/determinant.shader_test | 116 +++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 tests/hlsl/determinant.shader_test
diff --git a/Makefile.am b/Makefile.am index b963e4c03..780d1def9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,6 +86,7 @@ vkd3d_shader_tests = \ tests/hlsl/cross.shader_test \ tests/hlsl/d3dcolor-to-ubyte4.shader_test \ tests/hlsl/ddxddy.shader_test \ + tests/hlsl/determinant.shader_test \ tests/hlsl/discard.shader_test \ tests/hlsl/distance.shader_test \ tests/hlsl/dot.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 8cdc82263..aaa34ef71 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -3155,6 +3155,94 @@ static bool intrinsic_ddy_fine(struct hlsl_ctx *ctx, return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSY_FINE, arg, loc); }
+static bool intrinsic_determinant(struct hlsl_ctx *ctx, + const struct parse_initializer *params, const struct vkd3d_shader_location *loc) +{ + static const char determinant2x2[] = + "%s determinant(%s2x2 m)\n" + "{\n" + " return m._11 * m._22 - m._12 * m._21;\n" + "}"; + static const char determinant3x3[] = + "%s determinant(%s3x3 m)\n" + "{\n" + " %s2x2 m1 = { m._22, m._23, m._32, m._33 };\n" + " %s2x2 m2 = { m._21, m._23, m._31, m._33 };\n" + " %s2x2 m3 = { m._21, m._22, m._31, m._32 };\n" + " %s3 v1 = { m._11, -m._12, m._13 };\n" + " %s3 v2 = { determinant(m1), determinant(m2), determinant(m3) };\n" + " return dot(v1, v2);\n" + "}"; + static const char determinant4x4[] = + "%s determinant(%s4x4 m)\n" + "{\n" + " %s3x3 m1 = { m._22, m._23, m._24, m._32, m._33, m._34, m._42, m._43, m._44 };\n" + " %s3x3 m2 = { m._21, m._23, m._24, m._31, m._33, m._34, m._41, m._43, m._44 };\n" + " %s3x3 m3 = { m._21, m._22, m._24, m._31, m._32, m._34, m._41, m._42, m._44 };\n" + " %s3x3 m4 = { m._21, m._22, m._23, m._31, m._32, m._33, m._41, m._42, m._43 };\n" + " %s4 v1 = { m._11, -m._12, m._13, -m._14 };\n" + " %s4 v2 = { determinant(m1), determinant(m2), determinant(m3), determinant(m4) };\n" + " return dot(v1, v2);\n" + "}"; + static const char *templates[] = + { + [2] = determinant2x2, + [3] = determinant3x3, + [4] = determinant4x4, + }; + + struct hlsl_ir_node *arg = params->args[0]; + const struct hlsl_type *type = arg->data_type; + struct hlsl_ir_function_decl *func; + const char *typename, *template; + unsigned int dim; + char *body; + + if (type->class != HLSL_CLASS_SCALAR && type->class != HLSL_CLASS_MATRIX) + { + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid argument type."); + return false; + } + + dim = min(type->dimx, type->dimy); + if (dim == 1) + { + if (!(arg = intrinsic_float_convert_arg(ctx, params, arg, loc))) + return false; + return hlsl_add_load_component(ctx, params->instrs, arg, 0, loc); + } + + typename = type->base_type == HLSL_TYPE_HALF ? "half" : "float"; + template = templates[dim]; + + switch (dim) + { + case 2: + body = hlsl_sprintf_alloc(ctx, template, typename, typename); + break; + case 3: + body = hlsl_sprintf_alloc(ctx, template, typename, typename, typename, + typename, typename, typename, typename); + break; + case 4: + body = hlsl_sprintf_alloc(ctx, template, typename, typename, typename, + typename, typename, typename, typename, typename); + break; + default: + vkd3d_unreachable(); + } + + if (!body) + return false; + + func = hlsl_compile_internal_function(ctx, "determinant", body); + vkd3d_free(body); + if (!func) + return false; + + return add_user_call(ctx, func, params, loc); +} + static bool intrinsic_distance(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { @@ -4138,6 +4226,7 @@ intrinsic_functions[] = {"ddy_coarse", 1, true, intrinsic_ddy_coarse}, {"ddy_fine", 1, true, intrinsic_ddy_fine}, {"degrees", 1, true, intrinsic_degrees}, + {"determinant", 1, true, intrinsic_determinant}, {"distance", 2, true, intrinsic_distance}, {"dot", 2, true, intrinsic_dot}, {"exp", 1, true, intrinsic_exp}, diff --git a/tests/hlsl/determinant.shader_test b/tests/hlsl/determinant.shader_test new file mode 100644 index 000000000..d7dcaa563 --- /dev/null +++ b/tests/hlsl/determinant.shader_test @@ -0,0 +1,116 @@ +[pixel shader] +float s; + +float4 main() : sv_target +{ + return determinant(s); +} + +[test] +uniform 0 float4 9.0 2.0 3.0 4.0 +draw quad +probe all rgba (9.0, 9.0, 9.0, 9.0) + +[pixel shader] +float1x1 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 2.0 3.0 4.0 +draw quad +probe all rgba (1.0, 1.0, 1.0, 1.0) + +[pixel shader] +float2x2 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 2.0 3.0 4.0 +uniform 4 float4 5.0 6.0 7.0 8.0 +draw quad +probe all rgba (-4.0, -4.0, -4.0, -4.0) + +[pixel shader] +float2x1 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 2.0 3.0 4.0 +uniform 4 float4 5.0 6.0 7.0 8.0 +draw quad +probe all rgba (1.0, 1.0, 1.0, 1.0) + +[pixel shader] +float3x3 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 2.0 3.0 4.0 +uniform 4 float4 5.0 -6.0 7.0 8.0 +uniform 8 float4 9.0 10.0 11.0 12.0 +draw quad +probe all rgba (192.0, 192.0, 192.0, 192.0) + +[pixel shader] +float4x4 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 -2.0 3.0 4.0 +uniform 4 float4 5.0 6.0 -7.0 8.0 +uniform 8 float4 9.0 10.0 11.0 12.0 +uniform 12 float4 13.0 14.0 15.0 16.0 +draw quad +probe all rgba (-672.0, -672.0, -672.0, -672.0) + +[pixel shader] +float3x4 m; + +float4 main() : sv_target +{ + return determinant(m); +} + +[test] +uniform 0 float4 1.0 2.0 3.0 0.0 +uniform 4 float4 5.0 -6.0 7.0 0.0 +uniform 8 float4 9.0 10.0 11.0 0.0 +uniform 12 float4 0.0 0.0 0.0 0.0 +draw quad +probe all rgba (192.0, 192.0, 192.0, 192.0) + +[pixel shader fail] +float1 v; + +float4 main() : sv_target +{ + return determinant(v); +} + +[pixel shader fail] +float2 v; + +float4 main() : sv_target +{ + return determinant(v); +}
On Thu Mar 7 13:04:45 2024 +0000, Giovanni Mascellani wrote:
Friendly reminder that from my point of view this still needs to have `float` and `half` variants depending on the argument type. I don't care too much about the `printf()` variant that is used, but I would like to see `half` treated consistently with how we do in (most of) the rest of the HLSL compiler.
Thanks for the reminder. Updated now.
This merge request was approved by Giovanni Mascellani.
This merge request was approved by Henri Verbeet.