pdjstone / arculator-wasm

Arculator
http://b-em.bbcmicro.com/arculator
GNU General Public License v2.0
13 stars 2 forks source link

Investigate and improve input latency #2

Open pdjstone opened 1 year ago

pdjstone commented 1 year ago

The mouse latency in particular could be a lot better. Need to investigate why the lag occurs - whether it's due to the Emscripten/SDL or how Arculator is using SDL. Arculator running on native SDL has much better latency.

My understanding of how the main loop works when running in the browser wrt mouse:

In theory, we should be seeing 1 or 2 get_mickeys calls per frame because each browser frame is 16ms and get_mickeys is being called every 10 emulated ms. There should be new mouse event data every frame (i.e. before every call to arcloop) But we need to verify this.

One change I've already made from upstream Arculator is to change the order of calls in arc_run from:

execarm();
joystick_poll_host();
mouse_poll_host();
keyboard_poll_host();

to:

joystick_poll_host();
mouse_poll_host();
keyboard_poll_host();
execarm();

This should mean that mouse events get processed before the next frame is rendered, improving latency by one frame.

pdjstone commented 1 year ago

I added a flag to archimedes-live which should help with debugging and measuring latency. If you add #dbglatency to the URL, it will add some listeners to the mouse up/down/move events when the mouse is captured. The background will flash red when a mouse button is clicked and will flash blue when the mouse is moved. With a similar program running inside the emulator you can measure the delay between the browser getting mouse events and when the emulator does.

Both Chrome and Firefox have pretty good profilers built in which also record each graphics frame. Here's the Firefox recording - https://share.firefox.dev/3Xs1UL3. If you move over the Screenshots bar, you can see that the emulator background generally changes a single frame after browser background. This seems good but I've not really dug into the profile any further than that. Chrome seems to be about the same.

pdjstone commented 1 year ago

I tried reverting the above change - putting the execarm() call before the input polling and it does indeed make the latency worse by one frame (https://share.firefox.dev/3CGMYkc). Good to confirm that the change is an improvement.

pdjstone commented 1 year ago

I added some logging to print out the timestamps (performance.now()) of when a mousedown event is received in different parts of the code. I also logged the VIDC background colour before and after arc_run. This lets us see what's happening when the background goes red when the latency test program is running. I get the following logs:

JS mousedown 37252
emscripten_main mousedown before arc_run: 37255 vidc_bg: 00000000 
keyboard_poll mousedown: 37255 
emscripten_main mousedown after arc_run: 37261  vidc_bg: 000000ff

This shows that the mouse event is received by keyboard_poll in the same loop iteration that SDL receives it. It also shows that the VIDC background color changes to red in the same iteration. There are only 9ms between the first and last log line above. This suggests that the 1-frame latency is in the rendering code, not in the input pipeline.

matthewbloch commented 1 year ago

Finally sat down to measure with Is It Snappy on the iPhone which records at 240fps and lets me mark input & output, assuming you film the keyboard & screen. So in theory, it can measure at a resolution of 4.16ms.

So my simple test was to hit F12 and see how long til the command line * pops up, nice and obvious as it makes the whole screen jump. It's a bit less obvious to decide on which frame the input happened; I was just looking to see when my finger bounced off the bottom of the keypress. But still...

Under RISC OS 3.11 on the emscripten build on Firefox, I measured 191ms and 170ms on successive tests.

The same build on Windows, same source tree, I measured 125ms and 120ms.

For reference, on the same computer (Windows 11 laptop with an nVidia 2060) I loaded up indie banger Bezier because I know it's all hand-coded. I tried to measure the latency of moving between two menu items and mayyybe it was 29ms but I think I would need to improve my button-down detection because I was struggling to pin down the input frame and it could have been less. My monitor latency is quoted as 9ms.

So - 120ms on native seems like it has a lot of room for improvement, and Emscripten adds a (perfectly believable) >50% tax to that. I'd be interested to find some numbers for other emulators, and of course on native - if you have an iPhone and an A3000 plugged in to test :)

matthewbloch commented 1 year ago

On that basis I'd be interested in looking over the C codebase first to see why it's as sluggish as it is, because the emulation does seem to run at full speed.

But also I wonder whether it's possible to short-cut the SDL / Emscripten screen update loop and narrow the gap between native & web builds.

pdjstone commented 1 year ago

I spent a few evenings with Is It Snappy, testing a bunch of different things on different hardware, OSes and browsers. I put my results into a spreadsheet, and I'll try to summarise the trends:

I'm hoping we can still improve arculator-wasm latency slightly (i.e. by one frame / 16ms) however it seems that much of the latency is platform/browser/hardware/phase of the moon dependent!

matthewbloch commented 1 year ago

Weird! OK I mean that's encouraging. There is a bit of judgment in marking the input frame, but I'll try again with your method and add some rows to the spreadsheet. No wireless. I will try to replicate your measuremenets and using my (2020 Windows, Ryzen, nVidia) laptop alone.

pdjstone commented 1 year ago

On Chrome we may be able to use the pointerrawupdate event to get lower latency mouse input (see https://navidz.github.io/pointerrawupdate.html)