mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-01 15:39:47 +08:00
Apply SDWA ABS/NEG as float sign-bit, not integer, modifiers (#713)
SDWA's ABS and NEG source modifiers are floating-point sign-bit operations on GCN: ABS clears the sign bit, NEG flips it. We applied them as integer operations instead - SAbs, and a two's-complement negate of the raw bit pattern. That turns 1.0 into -4.0 and -3.0 into 1.5. UE4 compiles the final line of DrawRectangle, OutPosition.xy *= float2(1,-1), into a single V_MOV_B32 with SDWA NEG, so every UE fullscreen pass had its clip-space Y silently skewed. The canonical fullscreen triangle (1,-1) (-3,-1) (1,3) became (1,-4) (-3,-4) (1,1.5), which covers 6/11 of the viewport instead of all of it. That reproduces the measured defect exactly, on four independent quantities: hypotenuse slope 8/11, crossings of y=+1 and y=-1 at x=+7/11 and x=-9/11, and covered area 6/11 = 54.55% of the 2304x1296 viewport. It also explains why the edge was resolution-independent and identical across six unrelated shaders - it is the same instruction in every one of them. The distinguishing evidence is the transform's fixed point. A wrong scale would hold NDC -1 in x and +1 in y; the observed transform holds the opposite corner in both. Independently, and using only the measured line rather than any assumed vertex position: a wrong multiplier alone leaves a residual of -18 whatever the multiplier, and a wrong addend alone forces slope 1, not 8/11. Both terms had to be wrong at once, which only a Y-only sign-bit corruption produces. Float instructions are unaffected: GetFloatSource passes applySdwaIntegerModifiers: false and applies its own modifiers, so this path only ever fed raw-source reads - where the hardware behaviour is the sign-bit one regardless of the opcode being a bit-move. The sign bit is selected by the SDWA source-select width so a 16-bit select flips bit 15 rather than bit 31. Verified: emitted SPIR-V for the same shader changes from OpISub %uint %uint_0 %2147 to OpBitwiseXor %uint %2147 %uint_2147483648; 25-program synthetic conformance gate passes; 805 tests green; three 100-110s live runs with no crashes and no new shader failures. The end-to-end pixel re-measurement is NOT yet closed - see task #26.
This commit is contained in:
@@ -2706,20 +2706,31 @@ public static partial class Gen5SpirvTranslator
|
|||||||
|
|
||||||
if (applySdwaIntegerModifiers)
|
if (applySdwaIntegerModifiers)
|
||||||
{
|
{
|
||||||
|
// SDWA ABS/NEG are floating-point sign-bit modifiers even on
|
||||||
|
// a bit-move opcode: ABS clears the sign bit, NEG flips it.
|
||||||
|
// Two's-complement negating the raw bits instead turns 1.0
|
||||||
|
// into -4.0 and -3.0 into 1.5, which silently skews every
|
||||||
|
// pass that y-flips its clip position with an SDWA-negated
|
||||||
|
// V_MOV_B32 - the whole of UE's DrawRectangle.
|
||||||
|
var signBit = selector switch
|
||||||
|
{
|
||||||
|
<= 3 => 0x80u,
|
||||||
|
4 or 5 => 0x8000u,
|
||||||
|
_ => 0x80000000u,
|
||||||
|
};
|
||||||
|
|
||||||
if ((sdwa.AbsoluteMask & (1u << sourceIndex)) != 0)
|
if ((sdwa.AbsoluteMask & (1u << sourceIndex)) != 0)
|
||||||
{
|
{
|
||||||
value = Bitcast(
|
value = BitwiseAnd(value, UInt(~signBit));
|
||||||
_uintType,
|
|
||||||
Ext(5, _intType, Bitcast(_intType, value)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sdwa.NegateMask & (1u << sourceIndex)) != 0)
|
if ((sdwa.NegateMask & (1u << sourceIndex)) != 0)
|
||||||
{
|
{
|
||||||
value = _module.AddInstruction(
|
value = _module.AddInstruction(
|
||||||
SpirvOp.ISub,
|
SpirvOp.BitwiseXor,
|
||||||
_uintType,
|
_uintType,
|
||||||
UInt(0),
|
value,
|
||||||
value);
|
UInt(signBit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user