vkd3d-shader/hlsl: Properly lower casts to int for negative numbers.

While it looks complicated, it is what fxc/d3dcompiler does.

A shader as simple as:

    float4 f;

    float4 main() : sv_target
    {
        return (int4)f;
    }

results in the following instructions:

    ps_2_0
    def c1, 0, 1, 0, 0
    frc r0, c0
    cmp r1, -r0, c1.x, c1.y
    add r0, -r0, c0
    mov r2, c0
    cmp r1, r2, c1.x, r1
    add r0, r0, r1
    mov oC0, r0
This commit is contained in:
Francisco Casas 2025-01-15 10:45:39 -03:00 committed by Henri Verbeet
parent 4f7c117296
commit 2d91bd9200
Notes: Henri Verbeet 2025-01-16 19:29:44 +01:00
Approved-by: Elizabeth Figura (@zfigura)
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1339
2 changed files with 52 additions and 6 deletions

View File

@ -3107,11 +3107,24 @@ static bool sort_synthetic_separated_samplers_first(struct hlsl_ctx *ctx)
return false;
}
/* Turn CAST to int or uint into FLOOR + REINTERPRET (which is written as a mere MOV). */
/* Turn CAST to int or uint as follows:
*
* CAST(x) = x - FRACT(x) + extra
*
* where
*
* extra = FRACT(x) > 0 && x < 0
*
* where the comparisons in the extra term are performed using CMP.
*
* A REINTERPET (which is written as a mere MOV) is also applied to the final
* result for type consistency.
*/
static bool lower_casts_to_int(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block)
{
struct hlsl_ir_node *arg, *fract, *neg_fract, *has_fract, *floor, *extra, *res, *zero, *one;
struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = { 0 };
struct hlsl_ir_node *arg, *floor, *res;
struct hlsl_constant_value zero_value, one_value;
struct hlsl_ir_expr *expr;
if (instr->type != HLSL_IR_EXPR)
@ -3126,12 +3139,45 @@ static bool lower_casts_to_int(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
if (arg->data_type->e.numeric.type != HLSL_TYPE_FLOAT && arg->data_type->e.numeric.type != HLSL_TYPE_HALF)
return false;
if (!(floor = hlsl_new_unary_expr(ctx, HLSL_OP1_FLOOR, arg, &instr->loc)))
memset(&zero_value, 0, sizeof(zero_value));
if (!(zero = hlsl_new_constant(ctx, arg->data_type, &zero_value, &instr->loc)))
return false;
hlsl_block_add_instr(block, zero);
one_value.u[0].f = 1.0;
one_value.u[1].f = 1.0;
one_value.u[2].f = 1.0;
one_value.u[3].f = 1.0;
if (!(one = hlsl_new_constant(ctx, arg->data_type, &one_value, &instr->loc)))
return false;
hlsl_block_add_instr(block, one);
if (!(fract = hlsl_new_unary_expr(ctx, HLSL_OP1_FRACT, arg, &instr->loc)))
return false;
hlsl_block_add_instr(block, fract);
if (!(neg_fract = hlsl_new_unary_expr(ctx, HLSL_OP1_NEG, fract, &instr->loc)))
return false;
hlsl_block_add_instr(block, neg_fract);
if (!(has_fract = hlsl_new_ternary_expr(ctx, HLSL_OP3_CMP, neg_fract, zero, one)))
return false;
hlsl_block_add_instr(block, has_fract);
if (!(extra = hlsl_new_ternary_expr(ctx, HLSL_OP3_CMP, arg, zero, has_fract)))
return false;
hlsl_block_add_instr(block, extra);
if (!(floor = hlsl_new_binary_expr(ctx, HLSL_OP2_ADD, arg, neg_fract)))
return false;
hlsl_block_add_instr(block, floor);
if (!(res = hlsl_new_binary_expr(ctx, HLSL_OP2_ADD, floor, extra)))
return false;
hlsl_block_add_instr(block, res);
memset(operands, 0, sizeof(operands));
operands[0] = floor;
operands[0] = res;
if (!(res = hlsl_new_expr(ctx, HLSL_OP1_REINTERPRET, operands, instr->data_type, &instr->loc)))
return false;
hlsl_block_add_instr(block, res);

View File

@ -11,7 +11,7 @@ float4 main() : sv_target
[test]
uniform 0 float4 10.3 -11.6 12.8 13.1
draw quad
todo(sm<4) probe (0, 0) rgba(10, -11, 12, 0)
probe (0, 0) rgba(10, -11, 12, 0)
[vertex shader]
@ -32,7 +32,7 @@ float4 main(float4 t1 : TEXCOORD1) : sv_target
[test]
uniform 0 float4 -0.4 -0.7 -12.8 14.8
draw quad
todo(sm<4) probe (0, 0) rgba(0, 0, -12, 14)
probe (0, 0) rgba(0, 0, -12, 14)
[pixel shader todo(sm<4)]