Skip to content

Commit

Permalink
[metal] Improve layer initialization and resizing (gfx-rs#6107)
Browse files Browse the repository at this point in the history
* [metal]: Create a new layer instead of overwriting the existing one

Overriding the `layer` on `NSView` makes the view "layer-hosting", see
[wantsLayer], which disables drawing functionality on the view like
`drawRect:`/`updateLayer`.

This prevents crates like Winit from providing a robust rendering
callback that integrates well with the rest of the system.

Instead, if the layer is not CAMetalLayer, we create a new sublayer, and
render to that instead.

[wantsLayer]: https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer?language=objc

* [metal]: Fix double-free when re-using layer

* doc: Document the behavior when mis-configuring width/height of Surface

* [metal]: Use kCAGravityResize for smoother resizing

* [metal] Do not keep the view around that the surface was created from

We do not need to use it, and the layer itself is already retained, so
it won't be de-allocated from under our feet.

* Always set delegate on layers created by Wgpu

* More docs on contentsGravity
  • Loading branch information
madsmtm authored and ArthurBrussee committed Sep 15, 2024
1 parent d8c1fd6 commit 903bec5
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 113 deletions.
46 changes: 31 additions & 15 deletions wgpu-hal/src/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,32 @@ impl crate::Api for Api {
type AccelerationStructure = AccelerationStructure;
}

pub struct Instance {
managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate,
}
crate::impl_dyn_resource!(
Adapter,
AccelerationStructure,
BindGroup,
BindGroupLayout,
Buffer,
CommandBuffer,
CommandEncoder,
ComputePipeline,
Device,
Fence,
Instance,
PipelineCache,
PipelineLayout,
QuerySet,
Queue,
RenderPipeline,
Sampler,
ShaderModule,
Surface,
SurfaceTexture,
Texture,
TextureView
);

pub struct Instance {}

impl Instance {
pub fn create_surface_from_layer(&self, layer: &metal::MetalLayerRef) -> Surface {
Expand All @@ -88,9 +111,7 @@ impl crate::Instance for Instance {
profiling::scope!("Init Metal Backend");
// We do not enable metal validation based on the validation flags as it affects the entire
// process. Instead, we enable the validation inside the test harness itself in tests/src/native.rs.
Ok(Instance {
managed_metal_layer_delegate: surface::HalManagedMetalLayerDelegate::new(),
})
Ok(Instance {})
}

unsafe fn create_surface(
Expand All @@ -101,16 +122,12 @@ impl crate::Instance for Instance {
match window_handle {
#[cfg(target_os = "ios")]
raw_window_handle::RawWindowHandle::UiKit(handle) => {
let _ = &self.managed_metal_layer_delegate;
Ok(unsafe { Surface::from_view(handle.ui_view.as_ptr(), None) })
Ok(unsafe { Surface::from_view(handle.ui_view.cast()) })
}
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::AppKit(handle) => Ok(unsafe {
Surface::from_view(
handle.ns_view.as_ptr(),
Some(&self.managed_metal_layer_delegate),
)
}),
raw_window_handle::RawWindowHandle::AppKit(handle) => {
Ok(unsafe { Surface::from_view(handle.ns_view.cast()) })
}
_ => Err(crate::InstanceError::new(format!(
"window handle {window_handle:?} is not a Metal-compatible handle"
))),
Expand Down Expand Up @@ -343,7 +360,6 @@ pub struct Device {
}

pub struct Surface {
view: Option<NonNull<objc::runtime::Object>>,
render_layer: Mutex<metal::MetalLayer>,
swapchain_format: RwLock<Option<wgt::TextureFormat>>,
extent: RwLock<wgt::Extent3d>,
Expand Down
Loading

0 comments on commit 903bec5

Please sign in to comment.