-
Notifications
You must be signed in to change notification settings - Fork 71
Description
Winit currently implements redrawing in a weird ad-hoc fashion on macOS. This causes a bunch of issues, see rust-windowing/winit#2640 for some of it, which I intend to fix by emitting RedrawRequested in the built-in scheduled draw events (-[NSView drawRect:] / -[NSView updateLayer]) instead.
However, this won't work when the "consumer" of the raw-window-handle that Winit provides (i.e. blade) is overriding the layer on NSView, as is currently done, since that makes the view "layer-hosting", see -[NSView wantsLayer].
The code in question is around here:
blade/blade-graphics/src/metal/surface.rs
Lines 21 to 63 in 5da73b1
| pub unsafe fn from_view(view: *mut Object) -> Self { | |
| let main_layer: *mut Object = msg_send![view, layer]; | |
| let class = class!(CAMetalLayer); | |
| let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class]; | |
| let raw_layer = if is_valid_layer == YES { | |
| main_layer | |
| } else { | |
| // If the main layer is not a CAMetalLayer, we create a CAMetalLayer and use it. | |
| let new_layer: *mut Object = msg_send![class, new]; | |
| let frame: CGRect = msg_send![main_layer, bounds]; | |
| let () = msg_send![new_layer, setFrame: frame]; | |
| #[cfg(target_os = "ios")] | |
| { | |
| // Unlike NSView, UIView does not allow to replace main layer. | |
| let () = msg_send![main_layer, addSublayer: new_layer]; | |
| let () = msg_send![main_layer, setAutoresizingMask: 0x1Fu64]; | |
| let screen: *mut Object = msg_send![class!(UIScreen), mainScreen]; | |
| let scale_factor: CGFloat = msg_send![screen, nativeScale]; | |
| let () = msg_send![view, setContentScaleFactor: scale_factor]; | |
| }; | |
| #[cfg(target_os = "macos")] | |
| { | |
| let () = msg_send![view, setLayer: new_layer]; | |
| let () = msg_send![view, setWantsLayer: YES]; | |
| let () = msg_send![new_layer, setContentsGravity: kCAGravityTopLeft]; | |
| let window: *mut Object = msg_send![view, window]; | |
| if !window.is_null() { | |
| let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; | |
| let () = msg_send![new_layer, setContentsScale: scale_factor]; | |
| } | |
| } | |
| new_layer | |
| }; | |
| Self { | |
| view: msg_send![view, retain], | |
| render_layer: mem::transmute::<_, &metal::MetalLayerRef>(raw_layer).to_owned(), | |
| info: crate::SurfaceInfo { | |
| format: crate::TextureFormat::Rgba8Unorm, | |
| alpha: crate::AlphaMode::Ignored, | |
| }, | |
| } | |
| } |
To fix this, blade should be using -[CALayer addSublayer:] like on iOS instead of -[CALayer setLayer:]. The logic to get resizing to work automatically after this though is a bit cumbersome (you'll need to use observers), so I've extracted it into a crate: raw-window-metal.
I can submit a PR for it if you want, though note that raw-window-metal depends on objc2. If you don't want to use that, you could try to use a similar approach as wgpu currently does, though beware that that still has issues, observers are really the best way to do this (that I've found at least).