mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
iOS: fix fastmem NULL deref crash on low-memory devices and rotation layout break
Fix a NULL dereference crash in vtlb_RemoveFastmemMappings that occurred when the 4 GB fastmem virtual-address reservation failed on low-memory iOS devices such as the iPhone SE 2 under LiveContainer. The s_fastmem_virtual_mapping vector was never resized but was still indexed unconditionally by vtlb_Init through vtlb_VMapUnmap on every boot, dereferencing NULL. Add the same empty-vector early-return guard that the zero-arg overload already has, protecting both the boot path and the COP0 TLB-write runtime path. Fix the portrait and landscape rotation layout break by overriding viewWillTransition in ARMSX2HostingController to re-assign rootView inside the transition coordinator animation block, forcing UIHostingController to invalidate its internal sizing cache and re-measure for the new container size. Without this nudge, SwiftUI kept stale geometry after rotation, producing black bars, cropped viewports, and misplaced touch controls because the GeometryReader never updated and the Metal drawableSize stayed at the pre-rotation value. Revert the two-column pause menu layout in portrait for iPhones since the screen is too narrow even on Plus and Max devices, restoring single-column scroll while keeping two-column in landscape and on iPad.
This commit is contained in:
@@ -1006,6 +1006,13 @@ static void vtlb_RemoveFastmemMapping(u32 vaddr)
|
||||
|
||||
static void vtlb_RemoveFastmemMappings(u32 vaddr, u32 size)
|
||||
{
|
||||
// When the 4 GB fastmem area reservation fails (e.g. on low-memory iOS
|
||||
// devices like the iPhone SE 2), s_fastmem_virtual_mapping is never
|
||||
// resized and remains empty. Indexing it would dereference NULL, so bail
|
||||
// out early — there are no mappings to remove.
|
||||
if (s_fastmem_virtual_mapping.empty())
|
||||
return;
|
||||
|
||||
pxAssert((vaddr & VTLB_PAGE_MASK) == 0);
|
||||
pxAssert(size > 0 && (size & VTLB_PAGE_MASK) == 0);
|
||||
|
||||
|
||||
@@ -55,6 +55,30 @@ class ARMSX2HostingController<Content: View>: UIHostingController<Content> {
|
||||
applyNativeContentScale(to: view)
|
||||
}
|
||||
|
||||
/// Force SwiftUI to re-evaluate its layout when the device rotates.
|
||||
///
|
||||
/// This hosting controller is a child of SDL's root view controller. While
|
||||
/// UIKit does forward `viewWillTransition` to child controllers,
|
||||
/// `UIHostingController`'s internal layout engine sometimes fails to
|
||||
/// invalidate its `GeometryReader` contents promptly — especially when the
|
||||
/// view is pinned via Auto Layout constraints rather than living directly
|
||||
/// under the window. Without this nudge, SwiftUI keeps stale geometry after
|
||||
/// rotation, producing broken layouts (black bars, cropped viewports,
|
||||
/// misplaced touch controls). We force a re-evaluation by toggling the
|
||||
/// `rootView` on the animation coordinator so the re-layout rides the
|
||||
/// standard rotation animation block.
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
// Re-assigning rootView forces UIHostingController to invalidate its
|
||||
// internal sizing cache and re-measure for the new container size.
|
||||
let current = rootView
|
||||
coordinator.animate(alongsideTransition: { _ in
|
||||
self.rootView = current
|
||||
self.view.setNeedsLayout()
|
||||
self.view.layoutIfNeeded()
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func systemChromeNeedsUpdate() {
|
||||
setNeedsStatusBarAppearanceUpdate()
|
||||
setNeedsUpdateOfHomeIndicatorAutoHidden()
|
||||
|
||||
@@ -97,12 +97,13 @@ struct QuickMenuView: View {
|
||||
|
||||
/// Two columns only when the card is wide enough to keep both columns comfortable.
|
||||
/// iPad portrait and short/small phone-landscape devices fall back to one column.
|
||||
/// iPhone portrait uses two columns when wide enough (e.g. plus/max in portrait)
|
||||
/// so the pause menu content is compact and readable without scrolling.
|
||||
/// iPhone portrait always uses a single-column scroll layout — the screen is
|
||||
/// too narrow for two comfortable columns, even on Plus/Max devices. Landscape
|
||||
/// and iPad still get two columns when wide enough.
|
||||
private func supportsTwoColumns(width: CGFloat, height: CGFloat) -> Bool {
|
||||
switch variant {
|
||||
case .phonePortrait:
|
||||
return width >= 390
|
||||
return false
|
||||
case .ipadTwoColumn:
|
||||
return width >= 500 && height >= 320
|
||||
case .phoneLandscape:
|
||||
|
||||
Reference in New Issue
Block a user