diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 575289707b..b08c59a23a 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -348,6 +348,9 @@ void GLRenderManager::BeginFrame(bool enableProfiling) { curProgram_ = nullptr; #endif + // Shouldn't call BeginFrame unless we're in a run state. + _dbg_assert_(runCompileThread_); + int curFrame = GetCurFrame(); FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameIdGen_); @@ -368,10 +371,6 @@ void GLRenderManager::BeginFrame(bool enableProfiling) { frameData.readyForFence = false; } - if (!runCompileThread_) { - WARN_LOG(G3D, "BeginFrame while !run_!"); - } - insideFrame_ = true; } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index b423d5832a..5ae17fc84b 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1289,7 +1289,7 @@ void MainScreen::CreateViews() { } rightColumnChoices->Add(new Spacer(25.0)); -#if !PPSSPP_PLATFORM(IOS_APP_STORE) || defined(_DEBUG) +#if !PPSSPP_PLATFORM(IOS_APP_STORE) // Officially, iOS apps should not have exit buttons. Remove it to maximize app store review chances. rightColumnChoices->Add(new Choice(mm->T("Exit")))->OnClick.Handle(this, &MainScreen::OnExit); #endif diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 76e0603c9f..6345d08985 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -112,6 +112,7 @@ enum class EmuThreadState { STOPPED, }; +// OpenGL emu thread static std::thread emuThread; static std::atomic emuThreadState((int)EmuThreadState::DISABLED); @@ -1549,7 +1550,7 @@ static void ProcessFrameCommands(JNIEnv *env) { } } -std::thread g_vulkanRenderLoopThread; +std::thread g_renderLoopThread; static void VulkanEmuThread(ANativeWindow *wnd); @@ -1563,7 +1564,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runVulkanRenderLoo return false; } - if (g_vulkanRenderLoopThread.joinable()) { + if (g_renderLoopThread.joinable()) { ERROR_LOG(G3D, "runVulkanRenderLoop: Already running"); return false; } @@ -1577,7 +1578,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runVulkanRenderLoo return false; } - g_vulkanRenderLoopThread = std::thread(VulkanEmuThread, wnd); + g_renderLoopThread = std::thread(VulkanEmuThread, wnd); return true; } @@ -1586,11 +1587,11 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_requestExitVulkanR ERROR_LOG(SYSTEM, "Render loop already exited"); return; } - _assert_(g_vulkanRenderLoopThread.joinable()); + _assert_(g_renderLoopThread.joinable()); exitRenderLoop = true; - g_vulkanRenderLoopThread.join(); - _assert_(!g_vulkanRenderLoopThread.joinable()); - g_vulkanRenderLoopThread = std::thread(); + g_renderLoopThread.join(); + _assert_(!g_renderLoopThread.joinable()); + g_renderLoopThread = std::thread(); } // TODO: Merge with the Win32 EmuThread and so on, and the Java EmuThread? diff --git a/ios/AppDelegate.mm b/ios/AppDelegate.mm index d1b78c7344..b0c478166e 100644 --- a/ios/AppDelegate.mm +++ b/ios/AppDelegate.mm @@ -126,6 +126,8 @@ - (void)restart:(const char*)restartArgs { INFO_LOG(G3D, "Restart requested: %s", restartArgs); [self.viewController willResignActive]; + [self.viewController shutdown]; + self.window.rootViewController = nil; self.viewController = nil; // App was requested to restart, probably. @@ -136,12 +138,14 @@ // TODO: Ignoring the command line for now. // Hoping that overwriting the viewController works as expected... [self launchPPSSPP:0 argv:nullptr]; + [self.viewController didBecomeActive]; } --(void) applicationWillResignActive:(UIApplication *)application { +- (void)applicationWillResignActive:(UIApplication *)application { INFO_LOG(G3D, "willResignActive"); [self.viewController willResignActive]; + if (g_Config.bEnableSound) { iOSCoreAudioShutdown(); } @@ -149,7 +153,7 @@ System_PostUIMessage(UIMessage::LOST_FOCUS); } --(void) applicationDidBecomeActive:(UIApplication *)application { +- (void)applicationDidBecomeActive:(UIApplication *)application { INFO_LOG(G3D, "didBecomeActive"); if (g_Config.bEnableSound) { iOSCoreAudioInit(); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 8ccecd1bff..803c19b5e2 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -26,6 +26,7 @@ #include "Common/System/OSD.h" #include "Common/System/NativeApp.h" #include "Common/File/VFS/VFS.h" +#include "Common/Thread/ThreadUtil.h" #include "Common/Log.h" #include "Common/TimeUtil.h" #include "Common/Input/InputState.h" @@ -81,8 +82,9 @@ private: GLRenderManager *renderManager_; }; -static bool threadEnabled = true; -static bool threadStopped = false; +static std::atomic exitRenderLoop; +static std::atomic renderLoopRunning; +static std::thread g_renderLoopThread; id sharedViewController; @@ -90,7 +92,7 @@ id sharedViewController; ICadeTracker g_iCadeTracker; TouchTracker g_touchTracker; - GraphicsContext *graphicsContext; + IOSGLESContext *graphicsContext; LocationHelper *locationHelper; CameraHelper *cameraHelper; } @@ -145,6 +147,59 @@ extern float g_safeInsetBottom; } } +// The actual rendering is NOT on this thread, this is the emu thread +// that runs game logic. +void GLRenderLoop(IOSGLESContext *graphicsContext) { + SetCurrentThreadName("EmuThreadGL"); + renderLoopRunning = true; + + // graphicsContext->StartThread(); + NativeInitGraphics(graphicsContext); + + INFO_LOG(SYSTEM, "Emulation thread starting\n"); + while (!exitRenderLoop) { + NativeFrame(graphicsContext); + } + + INFO_LOG(SYSTEM, "Emulation thread shutting down\n"); + NativeShutdownGraphics(); + + // Also ask the main thread to stop, so it doesn't hang waiting for a new frame. + INFO_LOG(SYSTEM, "Emulation thread stopping\n"); + + exitRenderLoop = false; + renderLoopRunning = false; +} + +- (bool)runGLRenderLoop { + if (!graphicsContext) { + ERROR_LOG(G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context."); + return false; + } + + if (g_renderLoopThread.joinable()) { + ERROR_LOG(G3D, "runVulkanRenderLoop: Already running"); + return false; + } + + _dbg_assert_(!renderLoopRunning); + _dbg_assert_(!exitRenderLoop); + + g_renderLoopThread = std::thread(GLRenderLoop, graphicsContext); + return true; +} + +- (void)requestExitGLRenderLoop { + if (!renderLoopRunning) { + ERROR_LOG(SYSTEM, "Render loop already exited"); + return; + } + _assert_(g_renderLoopThread.joinable()); + exitRenderLoop = true; + g_renderLoopThread.join(); + _assert_(!g_renderLoopThread.joinable()); +} + - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self hideKeyboard]; @@ -201,24 +256,7 @@ extern float g_safeInsetBottom; [mBackGestureRecognizer setEdges:UIRectEdgeLeft]; [[self view] addGestureRecognizer:mBackGestureRecognizer]; - // Was previously DISPATCH_QUEUE_PRIORITY_HIGH. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NativeInitGraphics(graphicsContext); - - INFO_LOG(SYSTEM, "Emulation thread starting\n"); - while (threadEnabled) { - NativeFrame(graphicsContext); - } - - INFO_LOG(SYSTEM, "Emulation thread shutting down\n"); - NativeShutdownGraphics(); - - // Also ask the main thread to stop, so it doesn't hang waiting for a new frame. - INFO_LOG(SYSTEM, "Emulation thread stopping\n"); - graphicsContext->StopThread(); - - threadStopped = true; - }); + INFO_LOG(G3D, "Done with viewDidLoad. Next up, OpenGL"); } - (void)handleSwipeFrom:(UIScreenEdgePanGestureRecognizer *)recognizer @@ -238,31 +276,24 @@ extern float g_safeInsetBottom; [self shutdown]; } -- (void)willResignActive { - +- (void)didBecomeActive { + INFO_LOG(SYSTEM, "didBecomeActive begin"); + [self runGLRenderLoop]; + INFO_LOG(SYSTEM, "didBecomeActive end"); } -- (void)didBecomeActive { - +- (void)willResignActive { + INFO_LOG(SYSTEM, "willResignActive begin"); + [self requestExitGLRenderLoop]; + INFO_LOG(SYSTEM, "willResignActive end"); } - (void)shutdown { - if (sharedViewController == nil) { - return; - } - - iOSCoreAudioShutdown(); - - if (threadEnabled) { - threadEnabled = false; - while (graphicsContext->ThreadFrame()) { - continue; - } - while (!threadStopped) {} - graphicsContext->ThreadEnd(); - } + INFO_LOG(SYSTEM, "shutdown GL"); + _dbg_assert_(graphicsContext); + _dbg_assert_(sharedViewController != nil); sharedViewController = nil; if (self.context) { @@ -276,19 +307,15 @@ extern float g_safeInsetBottom; self.gameController = nil; - if (graphicsContext) { - graphicsContext->Shutdown(); - delete graphicsContext; - graphicsContext = NULL; - } - - NativeShutdown(); + graphicsContext->StopThread(); + graphicsContext->Shutdown(); + delete graphicsContext; + graphicsContext = nullptr; } - (void)dealloc { - INFO_LOG(SYSTEM, "dealloc"); - [self shutdown]; + INFO_LOG(SYSTEM, "dealloc GL"); } - (NSUInteger)supportedInterfaceOrientations @@ -298,8 +325,14 @@ extern float g_safeInsetBottom; - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { + if (!renderLoopRunning) { + INFO_LOG(G3D, "Ignoring drawInRect"); + return; + } + INFO_LOG(G3D, "drawInRect start"); if (sharedViewController) graphicsContext->ThreadFrame(); + INFO_LOG(G3D, "drawInRect end"); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event diff --git a/ios/ViewControllerMetal.mm b/ios/ViewControllerMetal.mm index 082487e573..1fd177a8f3 100644 --- a/ios/ViewControllerMetal.mm +++ b/ios/ViewControllerMetal.mm @@ -152,8 +152,6 @@ void IOSVulkanContext::Resize() { INFO_LOG(G3D, "IOSVulkanContext::Resize end (final size: %dx%d)", g_Vulkan->GetBackbufferWidth(), g_Vulkan->GetBackbufferHeight()); } -extern const char *PPSSPP_GIT_VERSION; - bool IOSVulkanContext::InitAPI() { INFO_LOG(G3D, "IOSVulkanContext::Init"); init_glslang(); @@ -203,9 +201,7 @@ bool IOSVulkanContext::InitAPI() { static std::atomic exitRenderLoop; static std::atomic renderLoopRunning; -static bool renderer_inited = false; -static std::mutex renderLock; -static std::thread g_vulkanRenderLoopThread; +static std::thread g_renderLoopThread; @interface PPSSPPViewControllerMetal () { ICadeTracker g_iCadeTracker; @@ -237,7 +233,8 @@ static std::thread g_vulkanRenderLoopThread; // Should be very similar to the Android one, probably mergeable. void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLayer) { - SetCurrentThreadName("EmuThread"); + SetCurrentThreadName("EmuThreadVulkan"); + INFO_LOG(G3D, "Entering EmuThreadVulkan"); if (!graphicsContext) { ERROR_LOG(G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context."); @@ -281,14 +278,8 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye // Gonna be in a weird state here.. } graphicsContext->ThreadStart(); - renderer_inited = true; - while (!exitRenderLoop) { - { - std::lock_guard renderGuard(renderLock); - NativeFrame(graphicsContext); - } - // Here Android processes frame commands. + NativeFrame(graphicsContext); } INFO_LOG(G3D, "Leaving Vulkan main loop."); } else { @@ -297,7 +288,6 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye NativeShutdownGraphics(); - renderer_inited = false; graphicsContext->ThreadEnd(); // Shut the graphics context down to the same state it was in when we entered the render thread. @@ -310,44 +300,73 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye } - (bool)runVulkanRenderLoop { + INFO_LOG(G3D, "runVulkanRenderLoop"); + if (!graphicsContext) { ERROR_LOG(G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context."); return false; } - if (g_vulkanRenderLoopThread.joinable()) { + if (g_renderLoopThread.joinable()) { ERROR_LOG(G3D, "runVulkanRenderLoop: Already running"); return false; } CAMetalLayer *metalLayer = (CAMetalLayer *)self.view.layer; - g_vulkanRenderLoopThread = std::thread(VulkanRenderLoop, graphicsContext, metalLayer); + g_renderLoopThread = std::thread(VulkanRenderLoop, graphicsContext, metalLayer); return true; } - (void)requestExitVulkanRenderLoop { + INFO_LOG(G3D, "requestExitVulkanRenderLoop"); + if (!renderLoopRunning) { ERROR_LOG(SYSTEM, "Render loop already exited"); return; } - _assert_(g_vulkanRenderLoopThread.joinable()); + _assert_(g_renderLoopThread.joinable()); exitRenderLoop = true; - g_vulkanRenderLoopThread.join(); - _assert_(!g_vulkanRenderLoopThread.joinable()); - g_vulkanRenderLoopThread = std::thread(); + g_renderLoopThread.join(); + _assert_(!g_renderLoopThread.joinable()); } // These two are forwarded from the appDelegate - (void)didBecomeActive { + INFO_LOG(G3D, "didBecomeActive GL"); + // Spin up the emu thread. It will in turn spin up the Vulkan render thread // on its own. [self runVulkanRenderLoop]; } - (void)willResignActive { + INFO_LOG(G3D, "willResignActive GL"); [self requestExitVulkanRenderLoop]; } +- (void)shutdown +{ + INFO_LOG(SYSTEM, "shutdown VK"); + + _dbg_assert_(sharedViewController != nil); + sharedViewController = nil; + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + self.gameController = nil; + + if (graphicsContext) { + graphicsContext->Shutdown(); + delete graphicsContext; + graphicsContext = NULL; + } +} + +- (void)dealloc +{ + INFO_LOG(SYSTEM, "dealloc VK"); +} + - (void)loadView { INFO_LOG(G3D, "Creating metal view"); @@ -393,14 +412,12 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye UIScreenEdgePanGestureRecognizer *mBackGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFrom:) ]; [mBackGestureRecognizer setEdges:UIRectEdgeLeft]; [[self view] addGestureRecognizer:mBackGestureRecognizer]; - } // Allow device rotation to resize the swapchain -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; // TODO: Handle resizing properly. - // demo_resize(&demo); } - (UIView *)getView { @@ -424,7 +441,9 @@ void VulkanRenderLoop(IOSVulkanContext *graphicsContext, CAMetalLayer *metalLaye - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; + INFO_LOG(G3D, "viewDidAppear"); [self hideKeyboard]; + INFO_LOG(G3D, "viewDidAppearDone"); } - (BOOL)prefersHomeIndicatorAutoHidden { diff --git a/ios/main.mm b/ios/main.mm index 8af1a372b0..48690865b2 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -31,6 +31,7 @@ #include "Common/System/Request.h" #include "Common/StringUtils.h" #include "Common/Profiler/Profiler.h" +#include "Common/Thread/ThreadUtil.h" #include "Core/Config.h" #include "Common/Log.h" #include "UI/DarwinFileSystemServices.h" @@ -550,6 +551,7 @@ void System_Vibrate(int mode) { int main(int argc, char *argv[]) { + // SetCurrentThreadName("MainThread"); version = [[[UIDevice currentDevice] systemVersion] UTF8String]; if (2 != sscanf(version.c_str(), "%d", &g_iosVersionMajor)) { // Just set it to 14.0 if the parsing fails for whatever reason.