phoboslab / wipeout-rewrite

2.61k stars 203 forks source link

float unrepresentable in integer range #48

Closed szotsaki closed 10 months ago

szotsaki commented 10 months ago

Minimal version, 40 seconds into the game, just trying it out. Chromium console shown:

wipeout-minimal.wasm:0x143b1 Uncaught RuntimeError: float unrepresentable in integer range
    at wipeout-minimal.wasm:0x143b1
    at wipeout-minimal.wasm:0x28ae
    at wipeout-minimal.wasm:0x17ec6
    at wipeout-minimal.wasm:0x55c01
    at wipeout-minimal.wasm:0x62329
    at wipeout-minimal.wasm:0x711bd
    at Module.dynCall_idi (wipeout-minimal.js:6789:39)
    at tick (wipeout-minimal.js:5133:13)

All settings were kind of random in the main menu, so it's possible that I cannot reproduce it. I was pressing x continuously and z multiple times. Hopefully, the screenshot helps a bit more to identify the location:

image

Assembly:

    local.set $var97
    local.get $var97
    f64.convert_i32_s
    local.set $var366
    local.get $var370
    local.get $var366
    f64.mul
    local.set $var373
    local.get $var282
    i32.load
    local.set $var98
    local.get $var98
    i32.const 2
    i32.div_s
    i32.const -1
    i32.and
    local.set $var231
    local.get $var231
    f64.convert_i32_s
    local.set $var368
    local.get $var373
    local.get $var368
    f64.sub
    local.set $var375
    local.get $var375
    i32.trunc_f64_s ; This is the problematic point at 0x143b1
    local.set $var228
    local.get $var267
    local.get $var228
    i32.store
    local.get $var267
    i32.const 4
    i32.add
    local.set $var299
    local.get $var278
    i32.const 4
    i32.add
    local.set $var316
    local.get $var316
    f32.load
    local.set $var328
    local.get $var328
    f32.neg
    local.set $var348
    local.get $var348
    f64.promote_f32
    local.set $var361
    local.get $var361
    f64.const 1
    f64.add
    local.set $var356
phoboslab commented 10 months ago

Difficult to track down without debug symbols :/

Questions:

Google isn't really that helpful for that error message. I assumed we're trying to convert a NaN to int somewhere, but in a quick test, converting a NaN to int results in 0 (not an error). Converting Inf to int results in integer overflow. So... not sure what exactly triggered this.

Edit: correction, converting Inf to int in Chrome (as opposed to Firefox) does result in float unrepresentable in integer range. So does converting e.g. 8589934592.0 (2^33) to int.

There seems to be a -s BINARYEN_TRAP_MODE=clamp flag for emcc to instead clamp those conversion to the 32 bit int range. But I'd rather like to know the culprit of this error.

phoboslab commented 10 months ago

I was able to track it down after all. Linking with -g2 was enough to get the function names in the WASM build, without shifting any offsets, so 0x143b1 was still the same code.

The bug was in hud_draw_target_icon() which draws the reticle when you have the missile or ebolt weapon. The reticle position is projected from world space into screen space, so we can draw it in the hud. If the projected z position is right on the screen plane (i.e. the target is directly right or left of you), the x position can get astronomically high and thus cause this overflow.

The above commit simply checks that 1) the target is in front of you, not behind and 2) the projection position is indeed on the screen. This happens before the conversion to integer pixel coordinates.

Thanks for the report! I learned something today :]

szotsaki commented 10 months ago

Nice catch and thank you for the quick fix!