diff --git a/Makefile.am b/Makefile.am index b963e4c0..780d1def 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 8cdc8226..aaa34ef7 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 00000000..d7dcaa56 --- /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); +}