gfx-rs / wgpu

A cross-platform, safe, pure-Rust graphics API.
https://wgpu.rs
Apache License 2.0
12.28k stars 901 forks source link

Surface lags behind on window resize #3756

Open nohenry opened 1 year ago

nohenry commented 1 year ago

Description When resizing the window, the wpgu surface does not keep up with the size of the window. This is possibly a problem with winit instead.

Repro steps Run any example in the wgpu/examples folder

Expected vs observed behavior I expect the surface to resize exactly to the current window dimensions:

https://user-images.githubusercontent.com/50533236/236695650-8513c44c-1d6d-4a2b-938a-3d436d090037.mov

But instead the surface lags behind the current window size:

https://user-images.githubusercontent.com/50533236/236695535-5674eca7-e02c-4d70-a277-1d2b57e86b35.mov

Platform This is running on MacOS Ventura (Metal). Using wgpu on v0.16 git I've not tested on Windows or Linux.

nohenry commented 1 year ago

Found the solution:

#[cfg(target_os = "macos")]
unsafe {
    surface
        .surface
        .as_hal_mut::<wgpu_hal::api::Metal, _, _>(|surface| {
            if let Some(surface) = surface {
                surface.present_with_transaction = true
            }
        });
}
kpreid commented 1 year ago

If it's that easy, perhaps this should be available as a safe operation. Reopen this issue?

ifacodes commented 1 year ago

If it's that easy, perhaps this should be available as a safe operation. Reopen this issue?

I agree, I feel like this issue has popped up a few times over the last few versions.

nohenry commented 1 year ago

One thing I want to know is if this a problem on different platforms (also how easy it would be to fix for them). I can test on Windows 11 and maybe Linux.

In terms of implementation, would it be best to have a field in the SurfaceConfiguration that determines this behaviour? I suppose it depends if this issue arises in the surface for other platforms.

kpreid commented 1 year ago

Another question is whether this should even be configurable, or just be the default. In other words: is there some situation where present_with_transaction should not be true? (For example, does it reduce performance when not resizing?)

cwfitzgerald commented 1 year ago

present_with_transaction afaik really limits the amount of frames in flight to under one, so unless you're making a UI app or other thing that needs to be resized all the time, it should be false so that the gpu can run independently.

One thing I want to know is if this a problem on different platforms (also how easy it would be to fix for them). I

@Ocrap7 there are issues on other platforms as resizing is an inherently racy problem. Afaik, DXGI can't do a truly smooth resize, though you can get close. Unfortunately the solutions will likely be different on each platform as each compositor is different.

timtom-dev commented 3 months ago

Any update on this? #3626 removed surface.as_hal_mut() so I've had to resort to this unholy abomination to get smooth resize on macOS:

#[allow(invalid_reference_casting)]
unsafe {
    surface.as_hal::<wgpu::hal::metal::Api, _, ()>(|surface| {
        if let Some(surface_ref) = surface {
            // AHH! Converting '&' to '&mut'
            let surface_mut = &mut *(surface_ref as *const wgpu::hal::metal::Surface as *mut wgpu::hal::metal::Surface);
            surface_mut.present_with_transaction = true;
        }
    });
}

which is obviously not ideal. 😬

madsmtm commented 1 month ago

I've improved the rendering when resizing in https://github.com/gfx-rs/wgpu/pull/6107, but using present_with_transaction is still needed to make it perfect.

Instead of exposing this to the user, perhaps we could enable it automatically when -[NSView inLiveResize] returns true? This might be more overhead because we'll be doing it on each render, though maybe we can cache the value of inLiveResize? Of course, this would ideally be done in -[NSView viewWillStartLiveResize] and -[NSView viewDidEndLiveResize], but those are harder for wgpu to get access to.