More app lifecycle work. Switching between Vulkan and OpenGL now works.

This commit is contained in:
Henrik Rydgård committed 2024-05-24 11:48:44 +02:00
1 parent 9a93a478d1
commit f864f52b64
7 files changed
+142 -84

No files matched your search

+3 -4
View File
@@ -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;
}
+1 -1
View File
@@ -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
+8 -7
View File
@@ -112,6 +112,7 @@ enum class EmuThreadState {
STOPPED,
};
// OpenGL emu thread
static std::thread emuThread;
static std::atomic<int> 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?
+6 -2
View File
@@ -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();
+81 -48
View File
@@ -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<bool> exitRenderLoop;
static std::atomic<bool> renderLoopRunning;
static std::thread g_renderLoopThread;
id<PPSSPPViewController> sharedViewController;
@@ -90,7 +92,7 @@ id<PPSSPPViewController> 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
+41 -22
View File
@@ -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<bool> exitRenderLoop;
static std::atomic<bool> 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<std::mutex> 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 {
+2
View File
@@ -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.