winit icon indicating copy to clipboard operation
winit copied to clipboard

winit-web: skip_recreation_check added

Open rickvanprim opened this issue 6 months ago • 20 comments

  • [x] Tested on all platforms changed
  • [x] Added an entry to the changelog module if knowledge of this change could be valuable to users
  • [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • [ ] Created or updated an example program if it would help users understand this functionality

Tested locally against a web app with two separate Canvases with their own winit+wgpu contexts in a Dioxus web app and unmounting+remounting the components (destroying and recreating everything in the process).

rickvanprim avatar Jun 08 '25 05:06 rickvanprim

The reason this is in place is for consistency between platforms. You can use spawn() to get the desired behavior.

Is there an issue with using spawn() for your use-case?

daxpedda avatar Jun 08 '25 08:06 daxpedda

The issue was mentioned in matrix, but basically we mark it for recreation as soon as you create the event loop, but spawn clears only after spawn/etc. Users can not run loops in parallel.

I'd generally lift the restriction on the event loop creation from at least the backend itself.

kchibisov avatar Jun 08 '25 12:06 kchibisov

Ah, I see, my bad, I didn't read the OP carefully enough as well.

I'd generally lift the restriction on the event loop creation from at least the backend itself.

Where else can it be enforced then? "at least" implies that it stays somewhere else, or did you mean you want to remove the restriction altogether?


I'm still of the same mind here: for consistency we shouldn't allow parallel event loop creation by default. Allowing it for spawn() or adding a backend specific escape hatch is what I believe we should do instead.

daxpedda avatar Jun 08 '25 17:06 daxpedda

How about something like allow_multiple: bool on winit-web::event_loop::PlatformSpecificEventLoopAttributes which could opt out of interacting with EVENT_LOOP_CREATED (both setting and clearing it)?

rickvanprim avatar Jun 08 '25 19:06 rickvanprim

That does sound fine for me.

However, I think I would prefer to guide users towards spawn() if at all possible for non-crossplatform behavior. Do you have a use-case specifically for using run()?

Also while we are at it, whats your use-case for running Winit in parallel?

daxpedda avatar Jun 08 '25 20:06 daxpedda

I'm not using run(), I'm using spawn_app(). I'm still not seeing a spawn() type API that let's me run multiple ApplicationHandlers in parallel. If I'm overlooking a solution that already exists, I'd be happy to switch to that 🙂

My use case is I'm trying to run multiple instances of what amounts to a game client side-by-side embedded in a Dioxus web app (so winit is not the root of the conceptual application) to represent each player's perspective. I could rework my ApplicationHandler type to be long lived and able to add/remove Window/Canvases dynamically as their respective Dioxus components are mounted/unmounted. However being able to isolate the full winit context and run multiple instances feels cleanest and lends itself towards being able to share the ApplicationHandler type between this "editor" usage and the normal "game/engine" usage.

Hopefully that makes sense. I'm happy to provide more info 🙂

rickvanprim avatar Jun 08 '25 20:06 rickvanprim

I'm not using run(), I'm using spawn_app(). I'm still not seeing a spawn() type API that let's me run multiple ApplicationHandlers in parallel. If I'm overlooking a solution that already exists, I'd be happy to switch to that 🙂

Apologies, my suggestion is that instead of adding an escape hatch to run() (via PlatformSpecificEventLoopAttributes), we can allow spawn_app() to run event loops in parallel by default. Right now spawn_app() allows re-creation by default, we should also make it allow parallel event loops by default.

I'm sorry for using the wrong method names, I'm a bit behind on all the renamed stuff.

Hopefully that makes sense. I'm happy to provide more info 🙂

I would recommend you to stick with a single event loop to reduce the overhead. However, the overhead is probably insignificant in comparison to a whole game and in comparison with the advantage of cleaner code.

Sounds cool though and makes sense to me!

daxpedda avatar Jun 08 '25 20:06 daxpedda

Apologies, my suggestion is that instead of adding an escape hatch to run() (via PlatformSpecificEventLoopAttributes), we can allow spawn_app() to run event loops in parallel by default. Right now spawn_app() allows re-creation by default, we should also make it allow parallel event loops by default.

My only concern with that is that there is an overlap there where this "could" snag. In practice this seems pretty unlikely and presumably easy to work around, but the following would fail:

let event_loop_1 = EventLoop::new().unwrap(); // recreation flag set
let event_loop_2 = EventLoop::new().unwrap(); // panic
event_loop_1.spawn_app(App1::default()); // recreation flag reset here
event_loop_2.spawn_app(App2::default());

If you're good with that, I'll update this PR to just clear the flag inside of spawn_app() instead of on drop 🙂 Or however you feel this is best implemented

rickvanprim avatar Jun 08 '25 21:06 rickvanprim

I'm thinking that the best implementation might be to make spawn_app() entirely ignore recreation flags and such instead of resetting it.


I was surprised that the current behavior was implemented this way in the first place: resetting the recreation flag instead of just ignoring it. So I went a back to the original PR https://github.com/rust-windowing/winit/pull/2897 and found https://github.com/rust-windowing/winit/pull/2907. Looking at https://github.com/rust-windowing/winit/issues/3311, it is probably best not to proceed here.

I apologize for stringing you along here, I completely forgot about all this. So unless we re-assess our position here, I'm currently in favor of not allowing multiple event loops in parallel.

daxpedda avatar Jun 08 '25 21:06 daxpedda

I'm not sure how handling CloseRequested is at odds with parallel EventLoops? I expect that multiple things on a given webpage could be registering to block navigating away/closing, including one or more EventLoops.

Anyways for my purposes, should I pivot to the long running singleton EventLoop, or wait for others to weigh in?

rickvanprim avatar Jun 08 '25 22:06 rickvanprim

Is it really limited if you create the web loop without going through the top-level event loop? I'm fine with e.g. winit-web to e.g. ignore all of that, but keep the checks around winit::event_loop, thus way we won't really change much.

kchibisov avatar Jun 08 '25 23:06 kchibisov

@kchibisov, assuming you're talking to me, are you suggesting I just directly create a winit-web::event_loop::ActiveEventLoop and call run() on it? Sounds fine to me. Currently run() is only pub(crate) though. Or do you mean something else?

rickvanprim avatar Jun 09 '25 01:06 rickvanprim

I meant to use winit-be::event_loop::EventLoop::new() and using it, and so also using direct methods on the web backend. Like the general limitation of the winit crate should be adjusted together with all the backends to preserve semantics, but backend itself may allow use you want.

kchibisov avatar Jun 09 '25 04:06 kchibisov

Gotcha. Sounds good to me 🙂

rickvanprim avatar Jun 09 '25 04:06 rickvanprim

I'm not sure how handling CloseRequested is at odds with parallel EventLoops? I expect that multiple things on a given webpage could be registering to block navigating away/closing, including one or more EventLoops.

The idea of allowing parallel EventLoops only makes sense to me if they don't interfere. In a case like CloseRequested they do, not necessarily because of blocking the interaction, but if we implement it before Firefox removes the whole B/F cache interaction (see #3311), we need a way to control setting the beforeunload event.

But there are other cases that would interfere in the future. When we change the window/surface model Web can implement more top-level methods, like changing the window title or window size. Moreover, the plan was in the future to replace multiple Windows in Web with subviews. So the only Window you can create on Web is the one covering the screen.

All of these things would significantly interfere with each other. Moreover I'm confident that parallel EventLoops as a feature is a convenience one and won't block anybody from doing anything specific.


Anyways for my purposes, should I pivot to the long running singleton EventLoop, or wait for others to weigh in?

I predict you should pivot. But as a contributor I've not contributed to Winit in a long time and I don't want to pretend I've the ultimate say here.

I meant to use winit-be::event_loop::EventLoop::new() and using it, and so also using direct methods on the web backend. Like the general limitation of the winit crate should be adjusted together with all the backends to preserve semantics, but backend itself may allow use you want.

This could be a viable solution. But it needs to be something else then winit_web::event_loop::EventLoop::new() with the interference I described above in mind. Something like SubViewLoop, or until sub views exist, WindowLoop.

daxpedda avatar Jun 09 '25 07:06 daxpedda

@kchibisov shall I close this PR?

rickvanprim avatar Jun 13 '25 03:06 rickvanprim

If you can achieve what you want by calling to winit-web directly, then I think yes?

kchibisov avatar Jun 22 '25 05:06 kchibisov

I cannot achieve that presently. I would need to expose something on winit-web. If you're fine with that, I'll take a shot at updating this PR.

rickvanprim avatar Jun 22 '25 05:06 rickvanprim

I think if the PR is directly against winit-web and not winit-core/etc it could be easier to move with it forward. I'm not web maintainer so can not say much about it, even though, I don't like that we have such recreation stuff for other backends as well, but some really need it, so it's in place for everyone as a lowest denominator.

kchibisov avatar Jun 22 '25 05:06 kchibisov

PR updated. If you don't want this touching winit-core at all, I can get rid of the EventLoopBuilderExtWeb stuff and just directly construct winit-web::event_loop::PlatformSpecificEventLoopAttributes and call winit-web::event_loop::EventLoop::new() with it.

rickvanprim avatar Jun 24 '25 17:06 rickvanprim

PR updated. If you don't want this touching winit-core at all, I can get rid of the EventLoopBuilderExtWeb stuff and just directly construct winit-web::event_loop::PlatformSpecificEventLoopAttributes and call winit-web::event_loop::EventLoop::new() with it.

Yeah, I guess the point is to have it entirely in winit-web.

kchibisov avatar Jun 28 '25 04:06 kchibisov

Just confirming, the usage of this will be:

winit::platform::web::EventLoop::new(&winit::platform::web::PlatformSpecificEventLoopAttributes {
  skip_recreation_check: true,
})?

Instead of the extension trait form:

winit::event_loop::EventLoop::builder().with_skip_recreation_check().build()?

This creates some other friction, because now I have update some method calls like event_loop.create_proxy() to event_loop.window_target().create_proxy() as the platform specific EventLoops evidently expose a slightly different API from the core one.

I've updated the PR removing the extension trait stuff. Let me know if you'd like any other changes 🙂

rickvanprim avatar Jul 03 '25 20:07 rickvanprim

Isn't it fine if it's a specific backend behavior that you should generally not rely upon, but just how the current backend may work? it's not a winit-core API for that reason, or winit API.

kchibisov avatar Jul 04 '25 15:07 kchibisov

Isn't it fine if it's a specific backend behavior that you should generally not rely upon, but just how the current backend may work? it's not a winit-core API for that reason, or winit API.

What does "should generally not rely upon" entail? Do you mean just because its backend specific?

In my explanation I pointed out why parallel event loops will conflict with each other in the future on Web, backend specifically.

daxpedda avatar Jul 04 '25 15:07 daxpedda

if in the future you would be able to achieve the desired functionality with the single event loop then I don't see an issue.

I'm not forcing any change here in particular though, just trying to find a compromise, since it's not the first time there's such request for web.

kchibisov avatar Jul 04 '25 15:07 kchibisov

if in the future you would be able to achieve the desired functionality with the single event loop then I don't see an issue.

OPs motivation is already achievable with the current API. The requested change is just for convenience. There is no plan to support running event loops in parallel on Web.

I proposed a hypothetical API for parallel event loops on Web in https://github.com/rust-windowing/winit/pull/4276#issuecomment-2954909303. However, I believe this would require not-insignificant amount of work and code to maintain. So IMO, unless there is a good use-case, we should not add that feature.

I'm not forcing any change here in particular though, just trying to find a compromise, since it's not the first time there's such request for web.

This request is about parallel event loops not about event loop re-creation, which is already covered with spawn_app(). I'm not aware of other user requests for parallel event loops on Web.

It would be good to know what other use-cases are out there.

daxpedda avatar Jul 05 '25 08:07 daxpedda

I think they just don't want to create them in sequence or IIRC the issue is that you can spawn_app if the previous one finished, so it seems like running side by side with spawn_app is not possible?

kchibisov avatar Jul 05 '25 09:07 kchibisov

Check https://github.com/rust-windowing/winit/pull/4276#issuecomment-2954264293 for the use-case.

But indeed, event loops doesn't support being run in parallel. But there is no use-case for running the event loop in parallel except for simpler implementations, ergo convenience. Multi-window API is harder than ApplicationHandler.

daxpedda avatar Jul 05 '25 10:07 daxpedda