Skip to content

Commit b3e5e0d

Browse files
Benoît Rouleaunot-fl3
authored andcommitted
ios: sync drawable size + emit Resize in drawableSizeWillChange
Per Apple's MTKView contract, `drawableSizeWillChange:` is invoked before the next `drawInMTKView:` whenever the drawable's pixel dimensions change (window resize, rotation under a non-locked orientation set, split-view drag on iPad). The previous commit added a no-op stub to satisfy the protocol; that prevents the "unrecognized selector" crash but leaves `native_display`'s stored dimensions stale until the next frame's `UIScreen.mainScreen.bounds` poll catches up. During iPad rotation animations the stored dimensions stay one frame behind the resized drawable. Anything that depends on the framebuffer dimensions for the current pass — `setScissorRect` is the common case — gets computed against the old size and fails Metal validation: -[MTLDebugRenderCommandEncoder setScissorRect:]: failed assertion `Set Scissor Rect Validation (rect.y(688) + rect.height(2064))(2752) must be <= render pass height(2064)' Update `native_display`'s `screen_width`/`screen_height` from the size argument immediately and send `Message::Resize` so the event handler resizes before the next draw. `draw_in_rect`'s own `UIScreen.bounds` poll still runs as a fallback (e.g. for full-screen size changes that don't go through the delegate).
1 parent bb99267 commit b3e5e0d

1 file changed

Lines changed: 24 additions & 12 deletions

File tree

src/native/ios.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,18 +376,30 @@ pub fn define_glk_or_mtk_view_dlg(superclass: &Class) -> *const Class {
376376
draw_in_rect(this, s, o, nil);
377377
}
378378

379-
// `MTKViewDelegate` requires both `drawInMTKView:` AND
380-
// `mtkView:drawableSizeWillChange:`. MTKView invokes the
381-
// size-change selector before the first `drawInMTKView:` after
382-
// any drawable-size change (window resize, rotation under a
383-
// non-locked orientation set, split-view drag on iPad). Without
384-
// an implementation, `_resizeDrawable` raises
385-
// `NSInvalidArgumentException` and crashes the process.
386-
//
387-
// The real resize handling lives in `draw_in_rect`, which polls
388-
// `UIScreen.mainScreen.bounds` every frame and emits
389-
// `Message::Resize` on a delta — so a stub is enough here.
390-
extern "C" fn drawable_size_will_change(_: &Object, _: Sel, _: ObjcId, _: NSSize) {}
379+
// `MTKViewDelegate` requires this alongside `drawInMTKView:`;
380+
// missing it crashes `_resizeDrawable` with
381+
// `NSInvalidArgumentException`. Sync `native_display` here so
382+
// the next frame's `setScissorRect` (and anything else reading
383+
// `screen_size`) matches the new drawable — `draw_in_rect`'s
384+
// `UIScreen.bounds` poll is the fallback but lags drawableSize
385+
// during rotation animations.
386+
extern "C" fn drawable_size_will_change(_: &Object, _: Sel, _: ObjcId, size: NSSize) {
387+
let width = size.width as i32;
388+
let height = size.height as i32;
389+
let changed = {
390+
let mut display = native_display().lock().unwrap();
391+
let changed =
392+
display.screen_width != width || display.screen_height != height;
393+
if changed {
394+
display.screen_width = width;
395+
display.screen_height = height;
396+
}
397+
changed
398+
};
399+
if changed {
400+
send_message(Message::Resize { width, height });
401+
}
402+
}
391403

392404
unsafe {
393405
decl.add_method(

0 commit comments

Comments
 (0)