Skip to content

Conversation

@paul2t
Copy link
Contributor

@paul2t paul2t commented Mar 5, 2025

Fixes #532

I actually attempted this migration 6 months ago, but failed because of a resize issue on wasm : 07a531e
Then I tried again a month ago and was able to do it but it required a lot of changes in the API, and using a lot of JS to callback and redraw. It wasn't practical.
Today, I am glad to say I fixed it completely: 7e329df
I also had to add a check of the size change in the render loop for some reason: 088d5d7 I'm not sure why, but without it, the rendering is not on the entire canvas surface, there is often a 1 or 2 pixels margin.

Now you can resize the window on the web. The canvas size can be driven by css. Note that max-width and max-height are required. Otherwise the size will be overwritten by winit.
I changed the index.html to make the canvas 50% of the size of the window for the wasm examples.

I tested a few examples on native and wasm. I noticed that change partially breaks the multiwindow native example. Now, when you are focused on a window, the other window freezes. I didn't take the time to investigate this issue. But I guess this is a minor issue. Which can be fixed later.

I also updated to egui 0.31 : de00c99

Copy link
Owner

@asny asny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nice work 🥳 thank you for doing this, I know it's not easy, especially with the major refactor that happened in winit 🙏

There's still some issues, but it's a great start! I've left some comments, but in general:

  • There's deprecated notices for some of the winit functionality, this might be related to multi window not working.
  • We can remove multi window example if it's too much work to fix, it's not a core part of what three-d should support.
  • There's a very different behaviour on web. We can change, but it would be nice to have at least somewhat the same behaviour.
  • Winit window example is not compiling on web.

LogicalSize::new(
browser_window.inner_width().unwrap().as_f64().unwrap(),
browser_window.inner_height().unwrap().as_f64().unwrap(),
)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This defaulted the canvas to cover the entire browser window (a kind of full screen mode), I guess that part is removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user can still configure the css to make the window take all of the available space.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, it's just a change in functionality, but I think it's ok 👍

use winit::platform::web::WindowExtWebSys;
self.window
.canvas()
_ = self.event_loop.run(move |event, event_loop| match event {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a deprecation warning about using this! Also, why is the result unhandled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is tricky. It would require to change the way three-d creates windows.
Because now you need to create a loop. And inside that loop, you can create the window. In the same way you are managing windows in the multi window example.
I didn't want to make changes to the three-d API for now.
It could be done later. I guess at some point winit is going to remove it, and we won't have a choice anymore. It needs to be planned.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really get the difference 🤔 The event loop is already created first, then the window, so what would the change be?


let winit_window = window_builder.build(&event_loop)?;
let winit_window = event_loop
.create_window(window_builder)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a deprecation warning from this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the other depreciation warning.

#[cfg(target_arch = "wasm32")]
{
self.gl.swap_buffers().unwrap();
// NOTE: This is needed because the canvas resize events are not all received.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's unfortunate that winit events are not working correctly 😞 But I think the fix is good then. However, I think it should just always make a resized event in each frame and then the frame input generator should check whether it actually has changed. That way, you don't need the window_width and window_height functionality on the frame input generator.

}
// WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// self.gl.resize(**new_inner_size);
// }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason scale factor changed is not handled?

#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
#[cfg(target_arch = "wasm32")]
use web_time::Instant;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this commit : rust-windowing/winit@5bbe879

pub(crate) fn window_height(&self) -> u32 {
self.window_height
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for these, see my comment above.

// self.viewport = Viewport::new_at_origo(new_inner_size.width, new_inner_size.height);
// let logical_size = new_inner_size.to_logical(self.device_pixel_ratio);
// self.window_width = logical_size.width;
// self.window_height = logical_size.height;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening here? 🤔

if let Some(text) = &event.text {
let mut s = String::new();
for ch in text.chars() {
if is_printable_char(ch) && !self.modifiers.ctrl && !self.modifiers.command
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this sanitization?

@paul2t
Copy link
Contributor Author

paul2t commented Mar 8, 2025

Thanks. I've replied to some of the comments. The other comments, I don't remember, I'll check again the reason for those changes.

  • We can remove multi window example if it's too much work to fix, it's not a core part of what three-d should support.

I'll take a look at how much work would be needed.

  • There's a very different behaviour on web. We can change, but it would be nice to have at least somewhat the same behaviour.

I think it's the same behavior. If you specify max_size, you get a window with this maximum size. If you don't, you get a window taking the whole available space, which can be specified via css. I think this last part is very important if you want to embed the three-d viewer in a web page. If you leave css unspecified, the viewer will take the entire window space.

  • Winit window example is not compiling on web.

Thanks. I'll take a look at it.

@asny
Copy link
Owner

asny commented Mar 8, 2025

Thanks. I've replied to some of the comments. The other comments, I don't remember, I'll check again the reason for those changes.

Perfect 👌

  • We can remove multi window example if it's too much work to fix, it's not a core part of what three-d should support.

I'll take a look at how much work would be needed.

Sounds like a good plan 👍

  • There's a very different behaviour on web. We can change, but it would be nice to have at least somewhat the same behaviour.

I think it's the same behavior. If you specify max_size, you get a window with this maximum size. If you don't, you get a window taking the whole available space, which can be specified via css. I think this last part is very important if you want to embed the three-d viewer in a web page. If you leave css unspecified, the viewer will take the entire window space.

With the same index.html file, there's still some differences between this branch (first screenshot) and master (second screenshot).

Screenshot 2025-03-08 at 19 40 21 Screenshot 2025-03-08 at 19 42 29

First, it seems like there's an issue with device pixel ratio/scale factor. three-d assumes viewports in physical pixels. Probably you have a screen with DPR=1, then it's really hard to test.

Second, there's a border around the canvas, where did that come from? 🤔

@joshburkart
Copy link
Contributor

Thank you for doing this @paul2t! Looking forward to the changes landing in main.

@prybicki
Copy link

prybicki commented Jul 3, 2025

@paul2t, @asny

Hi there, do you plan to make it into main? Winit 0.30 api looks much more convenient and I'd love to have it in three-d before writing a large amounts of event-handling code 😄

What is the remaining work here? I'm beginner in Rust, but I may look into it with some guidance.

@luxreduxdelux
Copy link

Hi, I just tested this and I always seem to be getting a SurfaceCreationError using this branch whereas I do not get any kind of error using the main, normal branch from asny. Will be glad to offer any kind of technical info if you would like to look into it.

@michael-p
Copy link

@asny @paul2t Thank you very much for your efforts and hard work on this! It'd be really great to get this merged, as winit 0.28 depends on the unmaintained instant crate, causing tools like cargo-deny to fail CI because of the RUSTSEC advisory.
If this migration is too much work in one go maybe update to winit 0.29 first (does not depend on instant anymore)?

@hug-dev
Copy link

hug-dev commented Oct 23, 2025

Hello 👋 !
Since I am also interested into this, I had a quick look at the branch and tried to understand what was remaining to do.
In order to move to 0.30 and to remove the deprecation notice mentioned above, a big change will be needed in the flow of how programs using winit are written. It's not very intuitive in the doc but one can't anymore create a Window from an event loop. As described in the Event Handling section of the doc, one needs to create a structure implementing the ApplicationHandler trait which contains two required methods:

  • resumed: as far as I understood this is where the window will be created and all the initialization steps requiring a WindowedContext.
  • window_event: the main event loop where everything is drawn and the camera moved

Based on what @paul2t has done so far, I could try to see how the examples would look like with the flow above.

I guess the other option would be to ignore the deprecation notice for now and continue with the current approach until the functions are removed.

edit: tried to do it only using non-deprecated method but hitting a wall... Here is the code for anyone wanting to try it. The main problem I see is how the main code is configured in the example. In the new winit, one can only get the window and the gl structure after the loop has run...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update winit dependency

7 participants