Skip to content
Snippets Groups Projects

vkd3d-shader/hlsl: Lower casts from/to ints for SM1.

Merged Francisco Casas requested to merge fcasas/vkd3d:snk_games2 into master
1 unresolved thread

NOTE: In the end I decided to send the first part of this MR as !616 (merged), already merged.

For temporary registers, SM1-SM3 integer types are internally represented as floating point, so, in order to perform a cast from ints to floats we need a mere MOV.

By the same token, casts from floats to ints can also be implemented with a FLOOR + MOV, where the FLOOR is then lowered by the lower_floor() pass.

For constant integer registers "iN" there is no operation for casting from a floating point register to them. For address registers "aN", and the loop counting register "aL", vertex shaders have the "mova" operation but we haven't used these registers in any way yet.

We probably would want to introduce these as synthetic variables allocated in a special register set. In that case we have to remember to use MOVA instead of MOV in the store operations, but they shouldn't be src or dst of CAST operations.

Regarding constant integer registers, in some shaders, constants are expected to be received formatted as an integer, such as:

int m;
float4 main() : sv_target
{
    float4 res = {0, 0, 0, 0};

    for (int k = 0; k < m; ++k)
        res += k;
    return res;
}

which compiles as:

// Registers:
//
//   Name         Reg   Size
//   ------------ ----- ----
//   m            i0       1
//

ps_3_0
def c0, 0, 1, 0, 0
mov r0, c0.x
mov r1.x, c0.x
rep i0
  add r0, r0, r1.x
  add r1.x, r1.x, c0.y
endrep
mov oC0, r0

but this only happens if the integer constant is used directly in an instruction that needs it, and as I said there is no instruction that allows converting them to a float representation.

Notice how a more complex shader, that performs operations with this integer variable "m":

int m;
float4 main() : sv_target
{
    float4 res = {0, 0, 0, 0};

    for (int k = 0; k < m * m; ++k)
        res += k;
    return res;
}

gives the following output:

// Registers:
//
//   Name         Reg   Size
//   ------------ ----- ----
//   m            c0       1
//

ps_3_0
def c1, 0, 0, 1, 0
defi i0, 255, 0, 0, 0
mul r0.x, c0.x, c0.x
mov r1, c1.y
mov r0.y, c1.y
rep i0
  mov r0.z, r0.x
  break_ge r0.y, r0.z
  add r1, r0.y, r1
  add r0.y, r0.y, c1.z
endrep
mov oC0, r1

Meaning that the uniform "m" is just stored as a floating point in "c0", the constant integer register "i0" is just set to 255 (hoping it is a high enough value) using "defi", and the "break_ge" involving c0 is used to break from the loop.

We could potentially use this approach to implement loops from SM3 without expecting the variables being received as constant integer registers.

According to the D3D documentation, for SM1-SM3 constant integer registers are only used by the 'loop' and 'rep' instructions.

Edited by Francisco Casas

Merge request reports

Checking pipeline status.

Merged by Alexandre JulliardAlexandre Julliard 1 year ago (Feb 15, 2024 10:59pm UTC)

Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
2655
2656 if (instr->type != HLSL_IR_EXPR)
2657 return false;
2658 expr = hlsl_ir_expr(instr);
2659 if (expr->op != HLSL_OP1_CAST)
2660 return false;
2661
2662 arg = expr->operands[0].node;
2663 if (instr->data_type->base_type != HLSL_TYPE_INT && instr->data_type->base_type != HLSL_TYPE_UINT)
2664 return false;
2665 if (arg->data_type->base_type != HLSL_TYPE_FLOAT && arg->data_type->base_type != HLSL_TYPE_HALF)
2666 return false;
2667
2668 /* Check that the argument is not already a FLOOR */
2669 if (arg->type == HLSL_IR_EXPR && hlsl_ir_expr(arg)->op == HLSL_OP1_FLOOR)
2670 return false;
  • Comment on lines +2668 to +2670

    It's ok for now, but it would be nice if this would eventually become another optimization pass, so that it covers other cases of double floors (or double whatever other idempotent operation).

  • Author Developer

    I wrote this part to ensure that the transform_ir() call using this pass doesn't return true every time it is called, in case it is used in a loop, but it makes sense to also have a pass to lower idempotent functions.

  • Please register or sign in to reply
  • Giovanni Mascellani approved this merge request

    approved this merge request

  • Henri Verbeet approved this merge request

    approved this merge request

  • Matteo Bruni approved this merge request

    approved this merge request

  • Alexandre Julliard approved this merge request

    approved this merge request

  • Please register or sign in to reply
    Loading