From f0a08826a3984e5bc5d794fcbf6f1864942e8c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Rouleau?= Date: Sat, 25 Jul 2026 22:27:24 -0400 Subject: [PATCH] ios: honour Conf::high_dpi on the Metal path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `create_metal_view` took `_high_dpi` and never used it. `MTKView` sizes its drawable from `bounds * contentScaleFactor`, which defaults to the screen's native scale, so `high_dpi: false` had no effect and the drawable was always native resolution. The display-size branch that reads `contentScaleFactor` then reported that same native size, so the two agreed only because neither honoured the flag. Applied once the view is in a window rather than at creation, for two reasons. Moving a view into a window re-derives its `contentScaleFactor` from that window's screen, so a value set at creation does not survive. And changing it resizes the drawable, whose `drawableSizeWillChange:` reaches for `NATIVE_DISPLAY` — which at creation time the backend has not initialized yet. Placed after `makeKeyAndVisible`, this covers the OpenGL view as well, which sets the scale at creation and loses it the same way. --- src/native/ios.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/native/ios.rs b/src/native/ios.rs index 78ba78dd..2913666f 100644 --- a/src/native/ios.rs +++ b/src/native/ios.rs @@ -503,7 +503,7 @@ unsafe fn create_opengl_view(screen_rect: NSRect, _sample_count: i32, high_dpi: } } -unsafe fn create_metal_view(screen_rect: NSRect, sample_count: i32, _high_dpi: bool) -> View { +unsafe fn create_metal_view(screen_rect: NSRect, sample_count: i32) -> View { let mtk_view_obj: ObjcId = msg_send![define_glk_or_mtk_view(class!(MTKView)), alloc]; let mtk_view_obj: ObjcId = msg_send![mtk_view_obj, initWithFrame: screen_rect]; @@ -586,7 +586,7 @@ pub fn define_app_delegate() -> *const Class { create_opengl_view(screen_rect, conf.sample_count, conf.high_dpi) } AppleGfxApi::Metal => { - create_metal_view(screen_rect, conf.sample_count, conf.high_dpi) + create_metal_view(screen_rect, conf.sample_count) } }; @@ -728,6 +728,14 @@ pub fn define_app_delegate() -> *const Class { msg_send_![window, addSubview: view]; msg_send_![window, setRootViewController: view_ctrl]; msg_send_![window, makeKeyAndVisible]; + + // `MTKView` loses a `contentScaleFactor` set before it has a window: moving it in + // re-derives the scale from that window's screen. A no-op for `GLKView`, which keeps + // what `create_opengl_view` set. Here rather than at creation because the resize this + // triggers reaches the backend, which is initialized by now. + if !crate::native_display().lock().unwrap().high_dpi { + msg_send_![view, setContentScaleFactor: 1.0]; + } } }