iOS: Resize in place on rotation instead of restarting graphics

Both view controllers stopped the emu/render thread on every rotation
and started it again, tearing down all of graphics. Since d0e45b94d9
NativeFrame resizes the graphics context and posts GPU_DISPLAY_RESIZED
on iOS too, so a NativeResized() is all a size change needs, like
Android now does. GLKView resizes its drawable by itself, and the Vulkan
context recreates the swapchain, which MoltenVK sizes from the layer.

The resize happens in viewDidLayoutSubviews, which UIKit runs at the
final size as a rotation animation starts. Resizing when the rotation
completed instead left half the screen black while it animated. This
also covers window resizes, which matter more now that
UIRequiresFullScreen is deprecated (iPadOS 26).

NativeFrame sets the PSP_CoreParameter() size, so the main thread
doesn't have to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-10 16:28:27 +02:00
1 parent b544f5cb4e
commit 69d33ef8e9
3 files changed
+19 -52

No files matched your search

-18
View File
@@ -259,24 +259,6 @@ PPSSPPBaseViewController *sharedViewController;
[(GLKView*)self.glView bindDrawable];
}
// Can't consolidate this yet.
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.view endEditing:YES]; // clears any input focus
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
NSLog(@"Rotating to size: %@", NSStringFromCGSize(size));
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
NSLog(@"Rotation finished");
// Reinitialize graphics context to match new size
[self requestExitGLRenderLoop];
[self updateResolutionWithView:self.view];
[self runGLRenderLoop];
}];
}
@end
void bindDefaultFBO()
+19 -4
View File
@@ -29,6 +29,7 @@
@implementation PPSSPPBaseViewController {
UIScreenEdgePanGestureRecognizer *mBackGestureRecognizer;
CGSize lastLayoutSize_;
}
// Strange idiom for generating unique IDs (within the process, at least).
@@ -523,6 +524,23 @@ extern float g_safeInsetBottom;
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// The immersive mode setting is per-orientation, so the status bar may need to change along with the rotation.
[self setNeedsStatusBarAppearanceUpdate];
[self.view endEditing:YES]; // clears any input focus
}
// Rotations and window resizes land here. UIKit lays out at the final size at the start of a
// rotation animation, so resizing here (rather than when the rotation completes) keeps the
// backbuffer matching the view while it animates.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGSize size = self.view.bounds.size;
if (size.width <= 0.0 || size.height <= 0.0 || CGSizeEqualToSize(size, lastLayoutSize_)) {
return;
}
lastLayoutSize_ = size;
INFO_LOG(Log::G3D, "Laid out at %dx%d", (int)size.width, (int)size.height);
// Resized in place: NativeFrame picks up the new size and lets the graphics context resize.
[self updateResolutionWithView:self.view];
}
- (void)updateResolutionWithView:(UIView *)view {
@@ -548,10 +566,7 @@ extern float g_safeInsetBottom;
[view setContentScaleFactor:scale];
// PSP native resize
PSP_CoreParameter().pixelWidth = g_display.pixel_xres;
PSP_CoreParameter().pixelHeight = g_display.pixel_yres;
// NativeFrame updates the PSP_CoreParameter() size along with the rest.
NativeResized();
NSLog(@"Updated display resolution: (%d, %d) @%.1fx",
-30
View File
@@ -220,36 +220,6 @@ static void VulkanRenderLoop(GraphicsContext *graphicsContext, CAMetalLayer *met
INFO_LOG(Log::G3D, "viewDidDisappear");
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// This is the first reliable place where self.view.bounds
// matches the forced orientation.
CGRect bounds = self.view.bounds;
INFO_LOG(Log::G3D, "Correcting metal view layout: %dx%d",
(int)bounds.size.width, (int)bounds.size.height);
// Update your Metal layer/viewport here if necessary
}
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.view endEditing:YES]; // clears any input focus
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
NSLog(@"Rotating to size: %@", NSStringFromCGSize(size));
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
NSLog(@"Rotation finished");
// Reinitialize graphics context to match new size
[self requestExitVulkanRenderLoop];
[self updateResolutionWithView:self.view];
[self runVulkanRenderLoop];
}];
}
@end
@implementation PPSSPPMetalView