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.
From: Nikolay Sivov nsivov@codeweavers.com
--- Makefile.am | 1 + libs/vkd3d-shader/hlsl.y | 70 ++++++++++++++++++++++++++++++ tests/hlsl/determinant.shader_test | 58 +++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 tests/hlsl/determinant.shader_test
diff --git a/Makefile.am b/Makefile.am index b36358b2..eebc359e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,6 +78,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 161d1ab4..13f94497 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2839,6 +2839,75 @@ 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[] = + "float determinant(%s arg)\n" + "{\n" + " float2x2 m = (float2x2)arg;\n" + " return m[0][0] * m[1][1] - m[0][1] * m[1][0];\n" + "}"; + static const char determinant3x3[] = + "float determinant(%s arg)\n" + "{\n" + " float3x3 m = (float3x3)arg;\n" + " float2x2 m1 = { m[1][1], m[1][2], m[2][1], m[2][2] };\n" + " float2x2 m2 = { m[1][0], m[1][2], m[2][0], m[2][2] };\n" + " float2x2 m3 = { m[1][0], m[1][1], m[2][0], m[2][1] };\n" + " float3 v1 = { m[0][0], -m[0][1], m[0][2] };\n" + " float3 v2 = { determinant(m1), determinant(m2), determinant(m3) };\n" + " return dot(v1, v2);\n" + "}"; + static const char determinant4x4[] = + "float determinant(%s arg)\n" + "{\n" + " float4x4 m = (float4x4)arg;\n" + " float3x3 m1 = { m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3] };\n" + " float3x3 m2 = { m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3] };\n" + " float3x3 m3 = { m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3] };\n" + " float3x3 m4 = { m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2] };\n" + " float4 v1 = { m[0][0], -m[0][1], m[0][2], -m[0][3] };\n" + " float4 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; + 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); + } + + if (!(body = hlsl_sprintf_alloc(ctx, templates[dim], type->name))) + 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) { @@ -3624,6 +3693,7 @@ intrinsic_functions[] = {"ddy", 1, true, intrinsic_ddy}, {"ddy_coarse", 1, true, intrinsic_ddy_coarse}, {"ddy_fine", 1, true, intrinsic_ddy_fine}, + {"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 00000000..0a596b46 --- /dev/null +++ b/tests/hlsl/determinant.shader_test @@ -0,0 +1,58 @@ +[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)
Zebediah Figura (@zfigura) commented about libs/vkd3d-shader/hlsl.y:
[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;
- 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;
- }
Can we have a bit more in the way of tests for this part? Tests for float, float1, float2, float1x1 would be nice at least.
Zebediah Figura (@zfigura) commented about libs/vkd3d-shader/hlsl.y:
- 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);
- }
- if (!(body = hlsl_sprintf_alloc(ctx, templates[dim], type->name)))
return false;
You already need to cast to float for dim == 1, so it seems to me it'd be the same amount of code to just always emit a cast to the requisite float matrix type, taking care of the base type conversion and casting down to a square matrix at once. Then you don't need to encode the type in the function body.