From b5c0c9c22f3c646cfc3143b5d8e6200a9a45ab6f Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Thu, 7 Sep 2023 02:09:36 +0200 Subject: [PATCH] vkd3d-shader/hlsl: Add fwidth() function. Signed-off-by: Nikolay Sivov --- 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 744e4612..11df76ad 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 a47246de..8c327c79 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -2961,6 +2961,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 (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) + return false; + type = params->args[0]->data_type; + + 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) { @@ -3690,6 +3717,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)