godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
87.26k stars 19.61k forks source link

Building with `proxy_to_pthread=yes` breaks WebXR #83733

Open dsnopek opened 9 months ago

dsnopek commented 9 months ago

Godot version

v4.2.beta.custom_build [f71f4b80e]

System information

Chromium-based browser (Brave) on the desktop, and Oculus browser on the Quest 2

Issue description

Trying any of my WebXR games with a web export template built with proxy_to_pthread=yes, I get a litany of these sort of errors from various places:

Caller thread can't call this function in this node (/root/XXX). Use call_deferred() or call_thread_group() instead.

... and the app doesn't work.

However, if I recompile with proxy_to_pthread=no, then everything works as expected.

I still need to dig in deeper to figure exactly what's happening here. I'm guess that when an event happens on the WebXR side, and it emits a signal that goes into Godot, it's coming from a different thread. So, perhaps using call_deferred() like the error says is the right fix? Or, maybe changing the *__proxy setting on some individual functions, now that that's actually being respected?

Related to https://github.com/godotengine/godot/pull/79711

Steps to reproduce

  1. Download the Godot XR Tools demo
  2. Export using an export template built with proxy_to_pthread=yes
  3. Open in browser and notice the errors in the console!

Minimal reproduction project

The Godot XR Tools demo can show the issue.

akien-mga commented 9 months ago

CC @adamscott @Faless

adamscott commented 9 months ago

I'm investigating.

dsnopek commented 9 months ago

I wonder if Emscripten no longer uses Window.requestAnimationFrame() for its main loop when building with PROXY_TO_PTHREAD? It would sort of make sense that it wouldn't be able to use that if not on the main thread. This documentation page seems to say as much:

Since requestAnimationFrame() API is not available in web workers, when called emscripten_set_main_loop() in a pthread with fps <= 0, the effect of syncing up to the display’s refresh rate is emulated, and generally will not precisely line up with vsync intervals.

Although, it says "in web workers" (which isn't our situation), and then "in a pthread" (which is our situation), so it's not totally clear.

However, if it's true that Emscripten doesn't use Window.requestAnimationFrame() for the main loop when using PROXY_TO_PTHREAD that means (1) we should test how well vsync is working when this is enabled because this emulation seems a little sketchy to me :-) and (2) I think PROXY_TO_PTHREAD may be fundamentally incompatible with WebXR.

The way that WebXR works, is that once you enter a WebXR session, you need to start using XRSession.requestAnimationFrame() (rather than the Window equivalent) so that frames are synchronized with the headset's refresh rate (rather than the screen refresh) and most WebXR API calls won't work unless they are made from within the callback passed to XRSession.requestAnimationFrame(). Unfortunately, there is no way to emulate this.

dsnopek commented 9 months ago

@adamscott and I discussed this on RocketChat over the weekend and concluded that it'd be best to change the default to proxy_to_pthread=no for now, in order to give us more time to figure this out. Adam said he'll make the PR for it.