Skip to content

Commit

Permalink
Fix Metal backend Surface::frome_uiview can not guarantee set correct…
Browse files Browse the repository at this point in the history
… view's contentScaleFactor (#2470)
  • Loading branch information
jinleili authored Feb 10, 2022
1 parent 6931e57 commit 48f8811
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 70 deletions.
6 changes: 3 additions & 3 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ impl crate::Instance<Api> 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),
}
Expand Down
100 changes: 33 additions & 67 deletions wgpu-hal/src/metal/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,88 +74,54 @@ 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");
}

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)
}
Expand Down

0 comments on commit 48f8811

Please sign in to comment.