mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Support screen rotation on iOS (unrestricted)
This commit is contained in:
@@ -8,9 +8,17 @@
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/Data/Collections/Hashmaps.h"
|
||||
|
||||
#if PPSSPP_PLATFORM(IOS)
|
||||
// iOS doesn't support a large number of timestamp queries natively.
|
||||
// We don't want MoltenVK to fall back to emulation.
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 32,
|
||||
};
|
||||
#else
|
||||
enum {
|
||||
MAX_TIMESTAMP_QUERIES = 128,
|
||||
};
|
||||
#endif
|
||||
|
||||
enum class VKRRunType {
|
||||
SUBMIT,
|
||||
|
||||
@@ -423,8 +423,9 @@ void VulkanRenderManager::StartThreads() {
|
||||
}
|
||||
}
|
||||
|
||||
// Called from main thread.
|
||||
// MUST be called from emuthread!
|
||||
void VulkanRenderManager::StopThreads() {
|
||||
INFO_LOG(Log::G3D, "VulkanRenderManager::StopThreads");
|
||||
// Make sure we don't have an open non-backbuffer render pass
|
||||
if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) {
|
||||
EndCurRenderStep();
|
||||
|
||||
@@ -200,7 +200,9 @@ void SaveFileInfo::DoState(PointerWrap &p)
|
||||
delete texture;
|
||||
texture = new PPGeImage("");
|
||||
}
|
||||
texture->DoState(p);
|
||||
if (texture) {
|
||||
texture->DoState(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +132,6 @@ bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const u8 *data_start = paramsfo + header->data_table_start;
|
||||
|
||||
auto readStringCapped = [paramsfo, size](size_t offset, size_t maxLen) -> std::string {
|
||||
std::string str;
|
||||
while (offset < size) {
|
||||
|
||||
@@ -614,7 +614,7 @@ void __DisplayFlip(int cyclesLate) {
|
||||
const double fpsLimit = FrameTimingLimit();
|
||||
bool throttle = fpsLimit != 0.0;
|
||||
|
||||
bool refreshRateNeedsSkip = fpsLimit != framerate && fpsLimit > refreshRate || !throttle;
|
||||
bool refreshRateNeedsSkip = (fpsLimit != framerate && fpsLimit > refreshRate) || !throttle;
|
||||
|
||||
g_frameTiming.ComputePresentMode(draw, refreshRateNeedsSkip);
|
||||
|
||||
|
||||
@@ -1332,19 +1332,23 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
|
||||
PopupSliderChoice *exitConfirmation = systemSettings->Add(new PopupSliderChoice(&g_Config.iAskForExitConfirmationAfterSeconds, 0, 1200, 60, sy->T("Ask for exit confirmation after seconds"), screenManager(), "s"));
|
||||
exitConfirmation->SetZeroLabel(sy->T("Off"));
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed", "Landscape Auto" };
|
||||
PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager()));
|
||||
#if PPSSPP_PLATFORM(IOS)
|
||||
// Portrait Reversed is not recommended on iPhone (and we also ban it in the plist).
|
||||
// However it's recommended to support it on iPad, so maybe we will in the future.
|
||||
rot->HideChoice(4);
|
||||
#endif
|
||||
|
||||
rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation);
|
||||
|
||||
if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) {
|
||||
systemSettings->Add(new CheckBox(&g_Config.bSustainedPerformanceMode, sy->T("Sustained performance mode")))->OnClick.Handle(this, &GameSettingsScreen::OnSustainedPerformanceModeChange);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings);
|
||||
systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));
|
||||
|
||||
@@ -52,8 +52,10 @@ static ImColor scaleColor(ImColor color, float factor) {
|
||||
}
|
||||
|
||||
bool ImDisasmView::getDisasmAddressText(u32 address, char *dest, size_t bufSize, bool abbreviateLabels, bool showData) {
|
||||
if (PSP_GetBootState() != BootState::Complete)
|
||||
if (PSP_GetBootState() != BootState::Complete) {
|
||||
dest[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetDisasmAddressText(address, dest, bufSize, abbreviateLabels, showData, displaySymbols_);
|
||||
}
|
||||
|
||||
@@ -107,4 +107,8 @@ __attribute__((used)) static Class _forceLinkSceneDelegate = [SceneDelegate clas
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
|
||||
return UIInterfaceOrientationMaskAll; // or at least include Portrait
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+2
-4
@@ -86,7 +86,6 @@ bool InitController(GCController *controller) {
|
||||
analogTriggerPressed(JOYSTICK_AXIS_RTRIGGER, value);
|
||||
};
|
||||
|
||||
#if defined(__IPHONE_12_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_1
|
||||
if ([extendedProfile respondsToSelector:@selector(leftThumbstickButton)] && extendedProfile.leftThumbstickButton != nil) {
|
||||
extendedProfile.leftThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
controllerButtonPressed(pressed, NKCODE_BUTTON_THUMBL);
|
||||
@@ -97,8 +96,7 @@ bool InitController(GCController *controller) {
|
||||
controllerButtonPressed(pressed, NKCODE_BUTTON_THUMBR);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
||||
|
||||
if ([extendedProfile respondsToSelector:@selector(buttonOptions)] && extendedProfile.buttonOptions != nil) {
|
||||
extendedProfile.buttonOptions.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
controllerButtonPressed(pressed, NKCODE_BUTTON_SELECT);
|
||||
@@ -109,7 +107,7 @@ bool InitController(GCController *controller) {
|
||||
controllerButtonPressed(pressed, NKCODE_BUTTON_START);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
|
||||
if ([extendedProfile respondsToSelector:@selector(buttonHome)] && extendedProfile.buttonHome != nil) {
|
||||
extendedProfile.buttonHome.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
|
||||
@@ -140,10 +140,6 @@
|
||||
- (void)updateResolution:(UIScreen *)screen {
|
||||
float scale = screen.nativeScale;
|
||||
CGSize size = screen.bounds.size;
|
||||
|
||||
if (size.height > size.width) {
|
||||
std::swap(size.height, size.width);
|
||||
}
|
||||
|
||||
float dpi;
|
||||
if (screen == [UIScreen mainScreen]) {
|
||||
|
||||
@@ -60,11 +60,13 @@
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UILaunchImageFile</key>
|
||||
<string>Default.png</string>
|
||||
|
||||
+1
-1
@@ -1,12 +1,12 @@
|
||||
// SceneDelegate.h
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ViewControllerCommon.h"
|
||||
|
||||
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
|
||||
|
||||
- (void)restart:(const char *)args;
|
||||
|
||||
@property (strong, nonatomic) UIWindow * window;
|
||||
@property (strong, nonatomic) UIViewController *viewController;
|
||||
@property (strong, nonatomic) UIWindowScene *windowScene;
|
||||
|
||||
@end
|
||||
|
||||
@@ -61,14 +61,12 @@
|
||||
// Choose viewcontroller depending on backend.
|
||||
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
|
||||
PPSSPPViewControllerMetal *vc = [[PPSSPPViewControllerMetal alloc] init];
|
||||
|
||||
self.viewController = vc;
|
||||
// sharedViewController gets initialized in the constructor.
|
||||
self.window.rootViewController = vc;
|
||||
|
||||
} else {
|
||||
PPSSPPViewControllerGL *vc = [[PPSSPPViewControllerGL alloc] init];
|
||||
// Here we can switch viewcontroller depending on backend.
|
||||
self.viewController = vc;
|
||||
self.window.rootViewController = vc;
|
||||
}
|
||||
|
||||
@@ -79,12 +77,11 @@
|
||||
INFO_LOG(Log::G3D, "SceneDelegate: Restart requested: %s", restartArgs);
|
||||
|
||||
// Notify current view controller
|
||||
[self.viewController willResignActive];
|
||||
[self.viewController shutdown];
|
||||
[sharedViewController willResignActive];
|
||||
[sharedViewController shutdown];
|
||||
|
||||
// Remove the current root view controller
|
||||
self.window.rootViewController = nil;
|
||||
self.viewController = nil;
|
||||
|
||||
INFO_LOG(Log::G3D, "SceneDelegate: viewController nilled");
|
||||
|
||||
@@ -95,13 +92,13 @@
|
||||
[self launchPPSSPP];
|
||||
|
||||
// Notify new view controller
|
||||
[self.viewController didBecomeActive];
|
||||
[sharedViewController didBecomeActive];
|
||||
}
|
||||
|
||||
- (void)sceneWillResignActive:(UIScene *)scene {
|
||||
INFO_LOG(Log::G3D, "sceneWillResignActive");
|
||||
|
||||
[self.viewController willResignActive];
|
||||
[sharedViewController willResignActive];
|
||||
|
||||
if (g_Config.bEnableSound) {
|
||||
iOSCoreAudioShutdown();
|
||||
@@ -119,6 +116,6 @@
|
||||
|
||||
System_PostUIMessage(UIMessage::GOT_FOCUS);
|
||||
|
||||
[self.viewController didBecomeActive];
|
||||
[sharedViewController didBecomeActive];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -390,9 +390,9 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
|
||||
INFO_LOG(Log::System, "dealloc GL");
|
||||
}
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
||||
{
|
||||
return UIInterfaceOrientationMaskLandscape;
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
|
||||
|
||||
+24
-26
@@ -53,7 +53,7 @@ public:
|
||||
void ShutdownFromRenderThread() override; // Inverses InitFromRenderThread.
|
||||
|
||||
void Shutdown() override;
|
||||
void Resize() override;
|
||||
void Resize() override {}
|
||||
|
||||
void *GetAPIContext() override { return g_Vulkan; }
|
||||
Draw::DrawContext *GetDrawContext() override { return draw_; }
|
||||
@@ -85,7 +85,11 @@ bool IOSVulkanContext::InitFromRenderThread(CAMetalLayer *layer, int desiredBack
|
||||
draw_ = Draw::T3DCreateVulkanContext(g_Vulkan, useMultiThreading);
|
||||
|
||||
VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_);
|
||||
if (!g_Vulkan->InitSwapchain(presentMode)) {
|
||||
|
||||
// This MUST run on the main thread. We're taking our chances with a dispatch_sync here.
|
||||
g_Vulkan->InitSwapchain(presentMode);
|
||||
|
||||
if (false) {
|
||||
delete draw_;
|
||||
ERROR_LOG(Log::G3D, "InitSwapchain failed");
|
||||
g_Vulkan->DestroySwapchain();
|
||||
@@ -126,19 +130,6 @@ void IOSVulkanContext::Shutdown() {
|
||||
INFO_LOG(Log::G3D, "IOSVulkanContext::Shutdown completed");
|
||||
}
|
||||
|
||||
void IOSVulkanContext::Resize() {
|
||||
INFO_LOG(Log::G3D, "IOSVulkanContext::Resize begin (oldsize: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
|
||||
draw_->HandleEvent(Draw::Event::LOST_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
g_Vulkan->DestroySwapchain();
|
||||
g_Vulkan->DestroySurface();
|
||||
g_Vulkan->ReinitSurface();
|
||||
VkPresentModeKHR presentMode = ConfigPresentModeToVulkan(draw_);
|
||||
g_Vulkan->InitSwapchain(presentMode);
|
||||
draw_->HandleEvent(Draw::Event::GOT_BACKBUFFER, g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
INFO_LOG(Log::G3D, "IOSVulkanContext::Resize end (final size: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight());
|
||||
}
|
||||
|
||||
bool IOSVulkanContext::InitAPI() {
|
||||
INFO_LOG(Log::G3D, "IOSVulkanContext::Init");
|
||||
init_glslang();
|
||||
@@ -232,6 +223,7 @@ static std::thread g_renderLoopThread;
|
||||
}
|
||||
|
||||
// Should be very similar to the Android one, probably mergeable.
|
||||
// This is the EmuThread for iOS.
|
||||
void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLayer) {
|
||||
SetCurrentThreadName("EmuThreadVulkan");
|
||||
INFO_LOG(Log::G3D, "Entering EmuThreadVulkan");
|
||||
@@ -435,12 +427,6 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye
|
||||
self.motionManager = [[CMMotionManager alloc] init];
|
||||
}
|
||||
|
||||
// Allow device rotation to resize the swapchain
|
||||
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
// TODO: Handle resizing properly.
|
||||
}
|
||||
|
||||
- (UIView *)getView {
|
||||
return [self view];
|
||||
}
|
||||
@@ -560,11 +546,6 @@ extern float g_safeInsetBottom;
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
{
|
||||
return UIInterfaceOrientationMaskLandscape;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
g_touchTracker.Began(touches, self.view);
|
||||
@@ -763,6 +744,23 @@ extern float g_safeInsetBottom;
|
||||
[self hideKeyboard];
|
||||
}
|
||||
|
||||
- (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 runVulkanRenderLoop];
|
||||
[[DisplayManager shared] updateResolution:[UIScreen mainScreen]];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PPSSPPMetalView
|
||||
|
||||
+1
-3
@@ -630,9 +630,7 @@ AudioBackend *System_CreateAudioBackend() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// SetCurrentThreadName("MainThread");
|
||||
int main(int argc, char *argv[]) {
|
||||
version = [[[UIDevice currentDevice] systemVersion] UTF8String];
|
||||
if (1 != sscanf(version.c_str(), "%d", &g_iosVersionMajor)) {
|
||||
// Just set it to 14.0 if the parsing fails for whatever reason.
|
||||
|
||||
Reference in New Issue
Block a user