-- v6: vkd3d-shader/hlsl: Support reflect() intrinsic.
From: Nikolay Sivov nsivov@codeweavers.com
--- Makefile.am | 1 + libs/vkd3d-shader/hlsl.y | 22 ++++++++++++++++++ tests/reflect.shader_test | 47 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/reflect.shader_test
diff --git a/Makefile.am b/Makefile.am index f9199472..8822e9eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,6 +130,7 @@ vkd3d_shader_tests = \ tests/preproc-invalid.shader_test \ tests/preproc-macro.shader_test \ tests/preproc-misc.shader_test \ + tests/reflect.shader_test \ tests/return.shader_test \ tests/round.shader_test \ tests/sampler.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 95c1c0fa..63576e6d 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2774,6 +2774,27 @@ static bool intrinsic_pow(struct hlsl_ctx *ctx, return true; }
+static bool intrinsic_reflect(struct hlsl_ctx *ctx, + const struct parse_initializer *params, const struct vkd3d_shader_location *loc) +{ + struct hlsl_ir_node *i = params->args[0], *n = params->args[1]; + struct hlsl_ir_node *dot, *mul_n, *two_dot, *neg; + + if (!(dot = add_binary_dot_expr(ctx, params->instrs, i, n, loc))) + return false; + + if (!(two_dot = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, dot, dot, loc))) + return false; + + if (!(mul_n = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, n, two_dot, loc))) + return false; + + if (!(neg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, mul_n, loc))) + return false; + + return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, i, neg, loc); +} + static bool intrinsic_round(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { @@ -2982,6 +3003,7 @@ intrinsic_functions[] = {"mul", 2, true, intrinsic_mul}, {"normalize", 1, true, intrinsic_normalize}, {"pow", 2, true, intrinsic_pow}, + {"reflect", 2, true, intrinsic_reflect}, {"round", 1, true, intrinsic_round}, {"saturate", 1, true, intrinsic_saturate}, {"sin", 1, true, intrinsic_sin}, diff --git a/tests/reflect.shader_test b/tests/reflect.shader_test new file mode 100644 index 00000000..8f601d24 --- /dev/null +++ b/tests/reflect.shader_test @@ -0,0 +1,47 @@ +[pixel shader] +uniform float4 i; +uniform float4 n; + +float4 main() : sv_target +{ + return reflect(i, n); +} + +[test] +uniform 0 float4 0.5 -0.1 0.2 0.3 +uniform 4 float4 0.6 0.4 -0.3 1.0 +draw quad +probe all rgba (-0.100000024, -0.5, 0.5, -0.7) 1 + +[pixel shader] +uniform float i; +uniform float4 n; + +float4 main() : sv_target +{ + return reflect(i, n); +} + +[test] +uniform 0 float4 0.5 -0.1 0.2 0.3 +uniform 4 float4 0.6 0.4 -0.3 1.0 +draw quad +probe all rgba (-0.5200001, -0.180000007, 1.00999999, -1.20000005) 1 + +[pixel shader fail] +uniform float2 i; +uniform float4 n; + +float4 main() : sv_target +{ + return reflect(i, n); +} + +[pixel shader fail] +uniform float3 i; +uniform float2 n; + +float4 main() : sv_target +{ + return reflect(i, n); +}
On Wed Feb 15 13:55:41 2023 +0000, Giovanni Mascellani wrote:
I'd like to see more tests that `reflect()` really behaves like on native with different types combinations. For example, what happens if you call `reflect()` on a `float` and a `float2`? Or on a `float2` and a `float3`? From my tests, it seems that the current implementation is correct, but it would be nice to have this shown by tests, so that we don't regress it in the future. It should be quite a quick thing to do.
Thanks. Please take a look.
Giovanni Mascellani (@giomasce) commented about tests/reflect.shader_test:
+uniform float2 i; +uniform float4 n;
+float4 main() : sv_target +{
- return reflect(i, n);
+}
+[pixel shader fail] +uniform float3 i; +uniform float2 n;
+float4 main() : sv_target +{
- return reflect(i, n);
+}
These two are slightly misleading. The call to `reflect()` is totally fine, the test fails because it cannot convert `float2` to `float4` when returning.
I am attaching the tests as I fixed them, which probably better pinpoint the behavior of `reflect()`: ``` [pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { return reflect(i, n); }
[test] uniform 0 float4 0.5 -0.1 0.2 0.3 uniform 4 float4 0.6 0.4 -0.3 1.0 draw quad probe all rgba (-0.1, -0.5, 0.5, -0.7) 4
[pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { float i1 = i.x;
return reflect(i1, n); }
[test] uniform 0 float4 0.5 0.0 0.0 0.0 uniform 4 float4 0.6 0.4 -0.3 1.0 draw quad probe all rgba (-0.52, -0.18, 1.01, -1.2) 4
[pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { float n1 = n.x;
return reflect(i, n1); }
[test] uniform 0 float4 0.5 -0.1 0.2 0.3 uniform 4 float4 0.6 0.0 0.0 0.0 draw quad probe all rgba (-0.148, -0.748, -0.448, -0.348) 4
[pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { float i1 = i.x; float n1 = n.x;
return reflect(i1, n1); }
[test] uniform 0 float4 0.5 0.0 0.0 0.0 uniform 4 float4 0.6 0.0 0.0 0.0 draw quad probe all rgba (0.14, 0.14, 0.14, 0.14) 4
[pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { float2 i1 = i.xy;
return float4(reflect(i1, n), 0.0, 0.0); }
[test] uniform 0 float4 0.5 -0.1 0.0 0.0 uniform 4 float4 0.6 0.4 -0.3 1.0 draw quad probe all rgba (0.188, -0.308, 0.0, 0.0) 4
[pixel shader] uniform float4 i; uniform float4 n;
float4 main() : sv_target { float3 i1 = i.xyz; float2 n1 = n.xy;
return float4(reflect(i1, n1), 0.0, 0.0); }
[test] uniform 0 float4 0.5 -0.1 0.2 0.0 uniform 4 float4 0.6 0.4 0.0 0.0 draw quad probe all rgba (0.188, -0.308, 0.0, 0.0) 4 ```
The reason why in some tests I declare a `float4` uniform and then use only some components is that if I declare a `float` uniform the layout might change between SM1 and SM4.