Windowing chapter won’t work on some platforms
JoeOsborn opened this issue · 4 comments
According to winit’s developers, it’s not permitted to create a surface on many platforms (Mac and android for example) until the Event::Resumed winit event is posted. This is a bummer because it means you need a mini state machine (I like to use an enum for this) to determine what phase in the initialization life cycle you’re in and you probably need a future executed for its side effect on web which means you need a OnceCell or something :/
I solved this a couple different ways in my modular renderer:
- if you have an executor already you can do something like the code sample below.
- If you are ok driving the single initialization future with the event loop you can do something like https://github.com/JoeOsborn/frenderer/blob/071beb5e547d3de2a6a03681926ca340bf5fbb75/frenderer/src/events.rs#L137 .
- It's also possible to put the GPU state behind a cell and just early-exit the closure if it hasn't been initialized yet, or lazily initialize the surface behind an option
let mut builder = Some(builder);
let fut = async {
let mut state = None;
let elp = winit::event_loop::EventLoop::new().unwrap();
let mut init: Option<State> = None;
elp.run(move |event, target| {
if let winit::event::Event::Resumed = event {
if let Some(builder) = builder.take() {
let window = Arc::new(builder.build(target).unwrap());
// maybe add canvas to window
// do state init and use .await freely in here
state = Some(...initialize Wgpu state and surface...)
}
}
if let Some(state) = state {
// match on event, do regular winit lifecycle stuff
}
})
.unwrap();
};
#[cfg(target_arch = "wasm32")]
{
wasm_bindgen_futures::spawn_local(fut);
}
#[cfg(not(target_arch = "wasm32"))]
{
pollster::block_on(fut);
}
The async block could instead just cover the WGPU initialization.
I'm pretty sure that you can't use .await
in the run
funciton as it's not async
. We technically only need to async
block to get the Adapter
, Device
and Queue
, so I could split up creating state into creating a Context
and creating the Display
.
Yeah, you’re totally right. I had mis-copied some code from another project. I think spawning a future on the resumed event (using wasm bindgen futures or a native executor or using the event loop itself to poll the future) which writes into a oncecell is one good option.
It’s really a pain that creating the surface must happen after the resumed event, and that sometimes creating an adapter without providing a surface causes issues too. If it weren’t for the latter you could make the adapter before entering the event loop, and lazily create the surface. But that can get you the wrong adapter sometimes.
I'm going to have to circle back to this. I'm working on updating to 0.19 and they changed Surface
to use the lifetime of the window. I tried use OnceCell
, but the borrow checker didn't approve. Once I get the 0.19 stuff working, I'll revisit this.
I think if your Window is an Arc, the surface gets a ‘static lifetime and is easier to work with.