From 6c323e23cfc2b7f9c610a92ea9cdde452d0d21d8 Mon Sep 17 00:00:00 2001 From: Jinlei Li Date: Thu, 10 Feb 2022 15:40:44 +0800 Subject: [PATCH] Fix Metal backend Surface::frome_uiview can not guarantee set correct view's contentScaleFactor --- wgpu-hal/src/metal/mod.rs | 6 +- wgpu-hal/src/metal/surface.rs | 100 +++++++++++----------------------- 2 files changed, 36 insertions(+), 70 deletions(-) diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 1dd3e252c2..7bf7977e3a 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -87,12 +87,12 @@ impl crate::Instance for Instance { #[cfg(target_os = "ios")] raw_window_handle::RawWindowHandle::UiKit(handle) => { let _ = &self.managed_metal_layer_delegate; - Ok(Surface::from_uiview(handle.ui_view)) + Ok(Surface::from_view(handle.ui_view, None)) } #[cfg(target_os = "macos")] - raw_window_handle::RawWindowHandle::AppKit(handle) => Ok(Surface::from_nsview( + raw_window_handle::RawWindowHandle::AppKit(handle) => Ok(Surface::from_view( handle.ns_view, - &self.managed_metal_layer_delegate, + Some(&self.managed_metal_layer_delegate), )), _ => Err(crate::InstanceError), } diff --git a/wgpu-hal/src/metal/surface.rs b/wgpu-hal/src/metal/surface.rs index 823b37ca2a..67c0c58b3b 100644 --- a/wgpu-hal/src/metal/surface.rs +++ b/wgpu-hal/src/metal/surface.rs @@ -74,10 +74,12 @@ impl super::Surface { } } - #[cfg(target_os = "ios")] #[allow(clippy::transmute_ptr_to_ref)] - pub unsafe fn from_uiview(uiview: *mut c_void) -> Self { - let view = uiview as *mut Object; + pub unsafe fn from_view( + view: *mut c_void, + delegate: Option<&HalManagedMetalLayerDelegate>, + ) -> Self { + let view = view as *mut Object; if view.is_null() { panic!("window does not have a valid contentView"); } @@ -85,77 +87,41 @@ impl super::Surface { 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 render_layer = if is_valid_layer == YES { mem::transmute::<_, &mtl::MetalLayerRef>(main_layer).to_owned() } else { - // If the main layer is not a CAMetalLayer, we create a CAMetalLayer sublayer and use it instead. - // Unlike on macOS, we cannot replace the main view as UIView does not allow it (when NSView does). + // If the main layer is not a CAMetalLayer, we create a CAMetalLayer and use it. let new_layer: mtl::MetalLayer = msg_send![class, new]; - let bounds: CGRect = msg_send![main_layer, bounds]; - let () = msg_send![new_layer.as_ref(), setFrame: bounds]; - let () = msg_send![main_layer, addSublayer: new_layer.as_ref()]; - new_layer - }; - - let window: *mut Object = msg_send![view, window]; - if !window.is_null() { - let screen: *mut Object = msg_send![window, screen]; - assert!(!screen.is_null(), "window is not attached to a screen"); - - let scale_factor: CGFloat = msg_send![screen, nativeScale]; - let () = msg_send![view, setContentScaleFactor: scale_factor]; - } - - let _: *mut c_void = msg_send![view, retain]; - Self::new(NonNull::new(view), render_layer) - } - - #[cfg(target_os = "macos")] - #[allow(clippy::transmute_ptr_to_ref)] - pub unsafe fn from_nsview( - nsview: *mut c_void, - delegate: &HalManagedMetalLayerDelegate, - ) -> Self { - let view = nsview as *mut Object; - if view.is_null() { - panic!("window does not have a valid contentView"); - } - - let class = class!(CAMetalLayer); - // Deprecated! Clients should use `create_surface_from_layer` instead. - let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class]; - if is_actually_layer == YES { - return Self::from_layer(mem::transmute(view)); - } - - let existing: *mut Object = msg_send![view, layer]; - let use_current = if existing.is_null() { - false - } else { - let result: BOOL = msg_send![existing, isKindOfClass: class]; - result == YES - }; - - let render_layer: mtl::MetalLayer = if use_current { - mem::transmute::<_, &mtl::MetalLayerRef>(existing).to_owned() - } else { - let layer: mtl::MetalLayer = msg_send![class, new]; - let () = msg_send![view, setLayer: layer.as_ref()]; - let () = msg_send![view, setWantsLayer: YES]; - let bounds: CGRect = msg_send![view, bounds]; - let () = msg_send![layer.as_ref(), setBounds: bounds]; - - let window: *mut Object = msg_send![view, window]; - if !window.is_null() { - let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; - let () = msg_send![layer, setContentsScale: scale_factor]; + let frame: CGRect = msg_send![main_layer, bounds]; + let () = msg_send![new_layer.as_ref(), setFrame: frame]; + #[cfg(target_os = "ios")] + { + // Unlike NSView, UIView does not allow to replace main layer. + let () = msg_send![main_layer, addSublayer: new_layer.as_ref()]; + // On iOS, "from_view" may be called before the application initialization is complete, + // `msg_send![view, window]` and `msg_send![window, screen]` will get null. + 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.as_ref()]; + let () = msg_send![view, setWantsLayer: YES]; + let () = msg_send![new_layer.as_ref(), 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]; + } + }; + if let Some(delegate) = delegate { + let () = msg_send![new_layer, setDelegate: delegate.0]; } - let () = msg_send![layer, setDelegate: delegate.0]; - layer + new_layer }; - let () = msg_send![render_layer, setContentsGravity: kCAGravityTopLeft]; - let _: *mut c_void = msg_send![view, retain]; Self::new(NonNull::new(view), render_layer) }