Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
-- v2: vkd3d-shader/hlsl: Add fwidth() function.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- Makefile.am | 1 + libs/vkd3d-shader/hlsl.y | 28 ++++++++++++++++++++++++++++ tests/hlsl/fwidth.shader_test | 26 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 tests/hlsl/fwidth.shader_test
diff --git a/Makefile.am b/Makefile.am index b36358b2..70829398 100644 --- a/Makefile.am +++ b/Makefile.am @@ -93,6 +93,7 @@ vkd3d_shader_tests = \ tests/hlsl/function-overload.shader_test \ tests/hlsl/function-return.shader_test \ tests/hlsl/function.shader_test \ + tests/hlsl/fwidth.shader_test \ tests/hlsl/gather-offset.shader_test \ tests/hlsl/gather.shader_test \ tests/hlsl/getdimensions.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 161d1ab4..0793d03d 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2957,6 +2957,33 @@ static bool intrinsic_frac(struct hlsl_ctx *ctx, return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_FRACT, arg, loc); }
+static bool intrinsic_fwidth(struct hlsl_ctx *ctx, + const struct parse_initializer *params, const struct vkd3d_shader_location *loc) +{ + struct hlsl_ir_function_decl *func; + struct hlsl_type *type; + char *body; + + static const char template[] = + "%s fwidth(%s x)\n" + "{\n" + " return abs(ddx(x)) + abs(ddy(x));\n" + "}"; + + if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc))) + return false; + type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); + + if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, type->name))) + return false; + func = hlsl_compile_internal_function(ctx, "fwidth", body); + vkd3d_free(body); + if (!func) + return false; + + return add_user_call(ctx, func, params, loc); +} + static bool intrinsic_ldexp(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { @@ -3631,6 +3658,7 @@ intrinsic_functions[] = {"floor", 1, true, intrinsic_floor}, {"fmod", 2, true, intrinsic_fmod}, {"frac", 1, true, intrinsic_frac}, + {"fwidth", 1, true, intrinsic_fwidth}, {"ldexp", 2, true, intrinsic_ldexp}, {"length", 1, true, intrinsic_length}, {"lerp", 3, true, intrinsic_lerp}, diff --git a/tests/hlsl/fwidth.shader_test b/tests/hlsl/fwidth.shader_test new file mode 100644 index 00000000..e302505f --- /dev/null +++ b/tests/hlsl/fwidth.shader_test @@ -0,0 +1,26 @@ +[require] +shader model >= 3.0 + +[pixel shader] +float4 main(float4 pos : sv_position) : sv_target +{ + // Shader models < 4 don't add 0.5 to sv_position, so this adjustment is required to get the + // same outputs. + pos.x = floor(pos.x) + 0.5; + pos.y = floor(pos.y) + 0.5; + + pos /= 10.0; + float nonlinear = pos.x * pos.y - pos.x * (pos.x + 0.5); + float4 res = fwidth(nonlinear); + + // Each device may use either the coarse or the fine derivate, so use quantization. + return round(30 * res); +} + +[test] +draw quad +probe (10, 10) rgba (8.0, 8.0, 8.0, 8.0) +probe (11, 10) rgba (8.0, 8.0, 8.0, 8.0) +probe (12, 10) rgba (10.0, 10.0, 10.0, 10.0) +probe (16, 16) rgba (12.0, 12.0, 12.0, 12.0) +probe (150, 150) rgba (92.0, 92.0, 92.0, 92.0)
On Thu Sep 7 17:21:43 2023 +0000, Zebediah Figura wrote:
Can we have a test, please? :-)
I added something based on existing ddx/ddy tests. I don't know how those derivatives work, but results match and pass for me on Windows.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/hlsl.y:
+static bool intrinsic_fwidth(struct hlsl_ctx *ctx,
const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
+{
- struct hlsl_ir_function_decl *func;
- struct hlsl_type *type;
- char *body;
- static const char template[] =
"%s fwidth(%s x)\n"
"{\n"
" return abs(ddx(x)) + abs(ddy(x));\n"
"}";
- if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc)))
return false;
What's the point of this with a single parameter? Am I missing anything?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/hlsl.y:
+static bool intrinsic_fwidth(struct hlsl_ctx *ctx,
const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
+{
- struct hlsl_ir_function_decl *func;
- struct hlsl_type *type;
- char *body;
- static const char template[] =
"%s fwidth(%s x)\n"
"{\n"
" return abs(ddx(x)) + abs(ddy(x));\n"
"}";
- if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc)))
return false;
- type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy);
As for the determinant, it is likely (though not necessarily true, so a test for that would be appropriate) that the right thing to do here is using `elementwise_intrinsic_float_convert_args()`.
On Fri Sep 8 16:07:03 2023 +0000, Giovanni Mascellani wrote:
As for the determinant, it is likely (though not necessarily true, so a test for that would be appropriate) that the right thing to do here is using `elementwise_intrinsic_float_convert_args()`.
I think the idea was to convert in add_user_call(), maybe it could be reworked.
On Fri Sep 8 16:07:02 2023 +0000, Giovanni Mascellani wrote:
What's the point of this with a single parameter? Am I missing anything?
Yes, it's not useful here.
On Fri Sep 8 16:09:37 2023 +0000, Nikolay Sivov wrote:
I think the idea was to convert in add_user_call(), maybe it could be reworked.
Yes, but you still have to detect the correct type to convert to. It seems that the general philosophy behind the HLSL compiler is that for expressions that only make sense for floating point numbers you convert every integral type (`int`, `uint` and `bool`) to `float`, and everything else doesn't need conversion. That's what `elementwise_intrinsic_float_convert_args()` does. I would assume that here the same happens, unless there is evidence of the contrary.
On Fri Sep 8 16:27:48 2023 +0000, Giovanni Mascellani wrote:
Yes, but you still have to detect the correct type to convert to. It seems that the general philosophy behind the HLSL compiler is that for expressions that only make sense for floating point numbers you convert every integral type (`int`, `uint` and `bool`) to `float`, and everything else doesn't need conversion. That's what `elementwise_intrinsic_float_convert_args()` does. I would assume that here the same happens, unless there is evidence of the contrary.
We probably need to fix lit() too now that you mention it.