Skip to content

Commit 28d2cde

Browse files
committed
Apply resizing delay patch gfx-rs#6107 (comment)
1 parent 8d3ade9 commit 28d2cde

File tree

1 file changed

+94
-11
lines changed

1 file changed

+94
-11
lines changed

wgpu-hal/src/metal/surface.rs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,63 @@ use alloc::borrow::ToOwned as _;
44
use core::mem::ManuallyDrop;
55
use core::ptr::NonNull;
66
use std::thread;
7+
use std::{borrow::ToOwned as _, sync::Once};
78

89
use core_graphics_types::{
910
base::CGFloat,
1011
geometry::{CGRect, CGSize},
1112
};
1213
use metal::{foreign_types::ForeignType, MTLTextureType};
14+
use objc::declare::ClassDecl;
15+
use objc::runtime::Sel;
1316
use objc::{
1417
class, msg_send,
1518
rc::{autoreleasepool, StrongPtr},
16-
runtime::{Object, BOOL, NO, YES},
19+
runtime::{Class, Object, BOOL, NO, YES},
1720
sel, sel_impl,
1821
};
1922
use parking_lot::{Mutex, RwLock};
2023

21-
use crate::metal::layer_observer::new_observer_layer;
22-
2324
#[link(name = "QuartzCore", kind = "framework")]
24-
extern "C" {}
25+
extern "C" {
26+
#[allow(non_upper_case_globals)]
27+
static kCAGravityResize: *mut Object;
28+
}
29+
30+
extern "C" fn layer_should_inherit_contents_scale_from_window(
31+
_: &Class,
32+
_: Sel,
33+
_layer: *mut Object,
34+
_new_scale: CGFloat,
35+
_from_window: *mut Object,
36+
) -> BOOL {
37+
YES
38+
}
39+
40+
static CAML_DELEGATE_REGISTER: Once = Once::new();
41+
42+
#[derive(Debug)]
43+
pub struct HalManagedMetalLayerDelegate(&'static Class);
44+
45+
impl HalManagedMetalLayerDelegate {
46+
pub fn new() -> Self {
47+
let class_name = format!("HalManagedMetalLayerDelegate@{:p}", &CAML_DELEGATE_REGISTER);
48+
49+
CAML_DELEGATE_REGISTER.call_once(|| {
50+
type Fun = extern "C" fn(&Class, Sel, *mut Object, CGFloat, *mut Object) -> BOOL;
51+
let mut decl = ClassDecl::new(&class_name, class!(NSObject)).unwrap();
52+
unsafe {
53+
// <https://developer.apple.com/documentation/appkit/nsviewlayercontentscaledelegate/3005294-layer?language=objc>
54+
decl.add_class_method::<Fun>(
55+
sel!(layer:shouldInheritContentsScale:fromWindow:),
56+
layer_should_inherit_contents_scale_from_window,
57+
);
58+
}
59+
decl.register();
60+
});
61+
Self(Class::get(&class_name).unwrap())
62+
}
63+
}
2564

2665
impl super::Surface {
2766
fn new(layer: metal::MetalLayer) -> Self {
@@ -98,14 +137,58 @@ impl super::Surface {
98137
// they expect us to do.
99138
unsafe { StrongPtr::retain(root_layer) }
100139
} else {
101-
// The view does not have a `CAMetalLayer` as the root layer (this
102-
// is the default for most views).
140+
// Create a new sublayer.
141+
let new_layer: *mut Object = msg_send![class!(CAMetalLayer), new];
142+
let () = msg_send![view.as_ptr(), setLayer: new_layer];
143+
144+
#[cfg(target_os = "macos")]
145+
if false {
146+
// Automatically resize the sublayer's frame to match the
147+
// superlayer's bounds.
148+
//
149+
// Note that there is a somewhat hidden design decision in this:
150+
// We define the `width` and `height` in `configure` to control
151+
// the `drawableSize` of the layer, while `bounds` and `frame` are
152+
// outside of the user's direct control - instead, though, they
153+
// can control the size of the view (or root layer), and get the
154+
// desired effect that way.
155+
//
156+
// We _could_ also let `configure` set the `bounds` size, however
157+
// that would be inconsistent with using the root layer directly
158+
// (as we may do, see above).
159+
let width_sizable = 1 << 1; // kCALayerWidthSizable
160+
let height_sizable = 1 << 4; // kCALayerHeightSizable
161+
let mask: std::ffi::c_uint = width_sizable | height_sizable;
162+
let () = msg_send![new_layer, setAutoresizingMask: mask];
163+
}
164+
165+
// Specify the relative size that the auto resizing mask above
166+
// will keep (i.e. tell it to fill out its superlayer).
167+
let frame: CGRect = msg_send![root_layer, bounds];
168+
let () = msg_send![new_layer, setFrame: frame];
169+
170+
// The gravity to use when the layer's `drawableSize` isn't the
171+
// same as the bounds rectangle.
103172
//
104-
// This case is trickier! We cannot use the existing layer with
105-
// Metal, so we must do something else. There are a few options,
106-
// we do the same as outlined in:
107-
// https://docs.rs/raw-window-metal/1.1.0/raw_window_metal/#reasoning-behind-creating-a-sublayer
108-
unsafe { new_observer_layer(root_layer) }
173+
// The desired content gravity is `kCAGravityResize`, because it
174+
// masks / alleviates issues with resizing when
175+
// `present_with_transaction` is disabled, and behaves better when
176+
// moving the window between monitors.
177+
//
178+
// Unfortunately, it also makes it harder to see changes to
179+
// `width` and `height` in `configure`. When debugging resize
180+
// issues, swap this for `kCAGravityTopLeft` instead.
181+
let _: () = msg_send![new_layer, setContentsGravity: unsafe { kCAGravityResize }];
182+
183+
// Set initial scale factor of the layer. This is kept in sync by
184+
// `configure` (on UIKit), and the delegate below (on AppKit).
185+
let scale_factor: CGFloat = msg_send![root_layer, contentsScale];
186+
let () = msg_send![new_layer, setContentsScale: scale_factor];
187+
188+
let delegate = HalManagedMetalLayerDelegate::new();
189+
let () = msg_send![new_layer, setDelegate: delegate.0];
190+
191+
unsafe { StrongPtr::new(new_layer) }
109192
}
110193
}
111194

0 commit comments

Comments
 (0)