BVE-Reborn / rend3

MAINTENCE MODE ---- Easy to use, customizable, efficient 3D renderer library built on wgpu.
https://rend3.rs
Apache License 2.0
1.05k stars 59 forks source link

Very slow event processing (seconds) #570

Closed John-Nagle closed 4 months ago

John-Nagle commented 5 months ago

The Windows event queue, as emulated under Wine, appears to work differently than the real one under Windows 10.

If you build my Rust test program, ui-mock, arc branch:

[code] git clone https://github.com/John-Nagle/ui-mock.git cd ui-mock git checkout arc cargo build --release --target x86_64-pc-windows-gnu --examples cd target/x86_64-pc-windows-gnu/release/examples wine ui-mock.exe [/code]

it will run fine under real Windows 10. But, built on Ubuntu 22.04 LTS with current Rust, it behaves strangely. Click on one of the big bars that says "placeholder", and you get get a login popup. It doesn't do anything; this is just a GUI dummy. Type something into the user name box.

On real Windows, characters are echoed at full speed.

[b]Under Wine 8 or 9, character echo is delayed about 3-4 seconds.[/b]

What seems to be happening is that the endless refreshing of the screen (this is a game-type program) is starving out event processing.

This uses the Rust crates Rend3, Winit, egui, and wgpu, all of which work cross-platform. A newer version of Rend3 (see the Cargo.toml file for the rev number) has a different approach to the event loop. There are two callback functions - "event", and "redraw" in Rend3's framework. The framework itself does a request-redraw at the end of each redraw. This seems to starve out non-redraw event processing.

An earlier version of Rend3 did not have this problem. That older version can be built as above, by removing the line

git checkout arc

This uses a different version of the Rend3 framework, one which only redraws when the event queue is empty.

"winit", a standard Rust crate, claims in its documentation that non-redraw events are always processed before redraws. But "winit", which has platform-dependent code for Windows, seems to assume, in the Windows platform code, that the underlying platform event queue does that. For real Windows, it does seem to do that. But Wine's event queue does not seem to work that way.

Also reported as a Wine bug: https://bugs.winehq.org/show_bug.cgi?id=56210

John-Nagle commented 4 months ago

I think this is broken due to changes in Winit's Event Loop 3.0. See

https://github.com/rust-windowing/winit/issues/2900

"Document that RedrawRequested is no longer guaranteed to be delivered in order with respect to any other event - it is platform specific".

I don't think this is working right for LInux, either - it takes several redraw cycles before keyboard input gets processed. Not as bad as Wine, though.

I think that implies that RedrawRequested should be called only in MainEventsCleared, to avoid problems. That's what the Winit docs seem to indicate is correct behavior.

dev-rain commented 4 months ago

Hi @John-Nagle,

Came by your issue here by reading winit el 3.0 thread which backlinks to this, I just wanted to clarify that it is indeed recommended that continuous rendering be achieved from RedrawRequested.

https://github.com/rust-windowing/winit/issues/2900#issuecomment-1694642963

In response to the bullet point you cited, as clarity on it has been needed before:

"Technically, yes NewEvents is going to be called before RedrawRequested, and AboutToWait should be called afterwards but there's no precise relationship guaranteed. NewEvents + AboutToWait may be dispatched many times e.g. due to input or other events and RedrawRequested isn't necessarily going to be called each time the event loop wakes up."

To paraphrase; here rib is clearing up that there is no guarantees about the ordering of delivery of a typical NewEvent -> RedrawRequested -> AboutToWait sequence might come in through each iteration of the event loop - but they will all come through on each iteration (be they intercepted by the OS internal loop or otherwise). Further to this from kchibisov:

Calling redraw_requested from RedrawRequested itself is supported and guaranteed to work on all the platforms. It'll properly send it on the next frame.

I don't mean to suggest that I can say for sure that wine isn't losing track of the winit event input somehow, and it would seem on the surface there is an apparent correlation between your issue and the old/new winit event loop.

I think that implies that RedrawRequested should be called only in MainEventsCleared, to avoid problems. That's what the Winit docs seem to indicate is correct behavior.

Yeah, the docs are out of step in places. MainEventsCleared no longer exists as per this: https://github.com/rust-windowing/winit/pull/2976.

Just wanted to clarify these points, not sure I actually helped except to say winit would expect that rend calls for redraws in RedrawRequested, and rend is doing so.

edit: fixed the link to pr2976

John-Nagle commented 4 months ago

Hm. I suspect, but am not sure, that Winit is relying on the underlying platform to un-duplicate redraw events. Wine may not be doing that. The Winit people say they don't test with Wine. The Wine people keep asking me for more info. Is there some way to get an event log out of Winit?

MainEventsCleared is not totally gone; it was renamed to AboutToWait, but current documentation discourages its use.

"and it would seem on the surface there is an apparent correlation between your issue and the old/new winit event loop."

There definitely is. You can build both and try them; they're on different branches of ui-mock, my dummy GUI test program.

John-Nagle commented 4 months ago

The Rend3 event loop now makes the request redraw call unconditionally. So I can't even try workarounds.

maroider commented 4 months ago

Is there some way to get an event log out of Winit?

Would this be a log of WM_* messages?

John-Nagle commented 4 months ago

Something like that. Then I could figure out if this is Winit or Wine or Rend3.

maroider commented 4 months ago

I basically haven't paid attention to Winit since mid-September last year, but there wasn't a built-in way to get a log like that at that time. I have this gist I used to paste in on the rare occasion I needed it myself. IIRC, you should be able to stick a call to dbg_msg here-ish, filling in the recurse_depth with userdata.recurse_depth.

John-Nagle commented 4 months ago

If I do that, I create an environment that others cannot reproduce, and the Wine people will close my bug report as UNCONFIRMED. In a finger-pointing situation like this, where Rend3, Winit, and Wine all blame each other, there has to be clarity.

John-Nagle commented 4 months ago

I've tested with a modified version of rend3-framework. I took out the call to window.request_redraw at the end of the framework's redraw. In the application, I added an event case for winit::event::Event::AboutToWait which calls window.request_redraw();

That fixed the slow echo.

So, regardless of claims to the contrary in the Winit documentation, doing request_redraw on every redraw doesn't work, and this does.

So I would like window.request_redraw taken out of the framework. That doesn't work as a mandatory action. Let the application control this.