wgpu icon indicating copy to clipboard operation
wgpu copied to clipboard

Surface lags behind on window resize

Open nohenry opened this issue 2 years ago • 8 comments

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 avatar May 07 '23 18:05 nohenry

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
            }
        });
}

nohenry avatar May 07 '23 20:05 nohenry

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

kpreid avatar May 07 '23 22:05 kpreid

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.

ifacodes avatar May 11 '23 18:05 ifacodes

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.

nohenry avatar May 12 '23 18:05 nohenry

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?)

kpreid avatar May 12 '23 22:05 kpreid

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.

cwfitzgerald avatar May 16 '23 15:05 cwfitzgerald

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. 😬

timtom-dev avatar Jun 17 '24 20:06 timtom-dev

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.

madsmtm avatar Aug 12 '24 15:08 madsmtm

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;
        }
    });
}

By adding this, not only do I no longer have any display problems, but the window now runs at a constant 120 fps compared with 60 without this setting. So thank you!

adrienreveleau avatar Jun 26 '25 18:06 adrienreveleau

Is it possible to make the present_with_transaction available to the users in some more straightforward way? For a egui project I ended up using a local fork of wgpu with present_with_transaction set to true in it's new() method and do not see a better way around it without forking anything. Maybe a configuration variable somewhere in upper levels of abstraction, like wgpu::SurfaceConfiguration?

aspcartman avatar Oct 14 '25 22:10 aspcartman