Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web worker support #1518

Closed
chemicstry opened this issue Mar 27, 2020 · 5 comments · Fixed by #2834
Closed

Web worker support #1518

chemicstry opened this issue Mar 27, 2020 · 5 comments · Fixed by #2834
Labels
C - needs discussion Direction must be ironed out DS - web F - question There's no such thing as a stupid one

Comments

@chemicstry
Copy link
Contributor

chemicstry commented Mar 27, 2020

Since web workers support canvas via OffscreenCanvas, would it be possible to make winit run in a web worker?

Main issues are these:

  • OffscreenCanvas implements EventTarget so events are not a problem. Although they will have to be forwarded from the main canvas via whatever worker implementation is used. Probably not a winit concern.
  • OffscreenCanvas is different type than HtmlCanvasElement and can not be converted in rust. However, since javascript is an interpreted language, inserting OffscreenCanvas instead of HtmlCanvasElement from js side does not cause a problem. Although browser throws an error due to missing functions (style(), request_fullscreen(), set_attribute()), adding empty stubs fixes the issue and it works happily. This is a bit hacky, but other option would require replacing backend::RawCanvasType with enum, which would require a lot of rewrites.
  • A lot of functions use web_sys::window() and panic on web worker. Could they instead of panicking return default value? Main offenders are scale_factor() and is_fullscreen() which both are not essential. However, if they return default value would it make debugging harder for regular canvas?

Maybe there's no point in running winit in web worker, because the only usable feature without additional glue is the event loop. But I opened this for discussion, as this is relevant for projects that use winit and trying to support wasm.

@ryanisaacg ryanisaacg added DS - web F - question There's no such thing as a stupid one C - needs discussion Direction must be ironed out labels Apr 6, 2020
@ryanisaacg
Copy link
Contributor

I guess my question would be why would you want to point winit at an offscreen canvas? Is it possible for that canvas to receive events?

@chemicstry
Copy link
Contributor Author

It does implement EventEmitter, but I believe input events would have to be forwarded from the main canvas via worker postMessage.

I just opened this issue to maybe explore possibilities and get ideas from other people on best practices with web assembly. There is a big issue with wasm that it supports threads (under web workers), with one big exception that you can't atomic wait (mutex, lock, sleep, etc) in main thread. This renders many native libraries and projects unable to run on wasm without major rewrites. So running entire application in a web worker is one way to overcome this problem.

We explored this option for https://github.com/amethyst/amethyst and had a PoC running with OffscreenCanvas so it is possible. However, web workers do not support audio and we had to abbandon this plan. Maybe this information will be useful for others attempting to port their projects into wasm and this issue can be closed. Or it could be left open as a low priority feature request.

@alvinhochun
Copy link
Contributor

I think for now it doesn't quite make sense for winit to actually do anything special for OffscreenCanvas. The winit event loop must run on the "main thread" to handle the DOM eevnts. While winit has control over the size of the canvas, the actual rendering is handled by the user. If the user would like to call transferControlToOffscreen and pass the OffscreenCanvas to a web worker, it's totally fine. The user is responsible for sending the winit events to the worker if the rendering code needs them.

This should be kind of similar to how OpenGL rendering on its own thread works on native. The user passes the OpenGL context to another thread and do the rendering over there, but the winit event loop still stays on the thread it is started on. The user will also need to send the winit events over with an MPSC or whatever.

IMO The current event handling and threading model on web makes it quite complicated to run the winit event loop on a web worker. I'm not convinced that running winit on a web worker is really a good approach.

@daxpedda
Copy link
Member

I didn't discover this issue before, but I have made some work towards supporting a multi-threaded Wasm environment.

I agree with @alvinhochun that I do not believe that winit should be run in a web worker, but it should be run in the window and send whatever needed over a channel to a web worker. To this end, I made some PR's that make it possible for Window and EventLoopProxy to be Send and Sync and be safely called from a web worker without causing panics by trying to access the JS window object:

The last bugs connected to transferControlToOffscreen() is that it will make some event handlers and Window methods panic when calling width() or height() on the original canvas. To this end I created #2733 to discuss a potential solution. See also gfx-rs/wgpu#3620.

@daxpedda
Copy link
Member

daxpedda commented Jun 5, 2023

This has been successfully resolved by #2834.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C - needs discussion Direction must be ironed out DS - web F - question There's no such thing as a stupid one
Development

Successfully merging a pull request may close this issue.

4 participants