wgpu
wgpu copied to clipboard
Surface lags behind on window resize
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.
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
}
});
}
If it's that easy, perhaps this should be available as a safe operation. Reopen this issue?
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.
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.
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?)
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.
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. 😬
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.
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!
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?