From 7c6a2b2c6207cd323b8ece8cbb4319c13a3fbed1 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 15 Jan 2015 08:02:21 +0100 Subject: [PATCH] Pause RGSS execution when moving into background on Android Assuming that there is enough memory for mkxp to stay in the background and that the OS doesn't kill the process, this should allow smooth resuming after moving back into the foreground. For now, EGL context loss is not handled. --- src/alstream.cpp | 3 + src/audio.cpp | 14 +++-- src/audio.h | 4 +- src/eventthread.cpp | 137 ++++++++++++++++++++++++++++++++++++++++++++ src/eventthread.h | 37 ++++++++++++ src/graphics.cpp | 22 +++++++ src/sharedstate.cpp | 2 +- 7 files changed, 212 insertions(+), 7 deletions(-) diff --git a/src/alstream.cpp b/src/alstream.cpp index da1e844..b0ca1b1 100644 --- a/src/alstream.cpp +++ b/src/alstream.cpp @@ -23,6 +23,7 @@ #include "sharedstate.h" #include "sharedmidistate.h" +#include "eventthread.h" #include "filesystem.h" #include "aldatasource.h" #include "fluid-fun.h" @@ -361,6 +362,8 @@ void ALStream::streamData() * refill and queue them up again */ while (true) { + shState->rtData().syncPoint.passSecondarySync(); + ALint procBufs = AL::Source::getProcBufferCount(alSrc); while (procBufs--) diff --git a/src/audio.cpp b/src/audio.cpp index aa16e7d..945aed0 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -25,6 +25,7 @@ #include "soundemitter.h" #include "sharedstate.h" #include "sharedmidistate.h" +#include "eventthread.h" #include "sdl-util.h" #include @@ -40,6 +41,8 @@ struct AudioPrivate SoundEmitter se; + SyncPoint &syncPoint; + /* The 'MeWatch' is responsible for detecting * a playing ME, quickly fading out the BGM and * keeping it paused/stopped while the ME plays, @@ -60,11 +63,12 @@ struct AudioPrivate MeWatchState state; } meWatch; - AudioPrivate(const Config &conf) + AudioPrivate(RGSSThreadData &rtData) : bgm(ALStream::Looped, "bgm"), bgs(ALStream::Looped, "bgs"), me(ALStream::NotLooped, "me"), - se(conf) + se(rtData.config), + syncPoint(rtData.syncPoint) { meWatch.state = MeNotPlaying; meWatch.thread = createSDLThread @@ -84,6 +88,8 @@ struct AudioPrivate while (true) { + syncPoint.passSecondarySync(); + if (meWatch.termReq) return; @@ -231,8 +237,8 @@ struct AudioPrivate } }; -Audio::Audio(const Config &conf) - : p(new AudioPrivate(conf)) +Audio::Audio(RGSSThreadData &rtData) + : p(new AudioPrivate(rtData)) {} diff --git a/src/audio.h b/src/audio.h index ea2a357..b0aec26 100644 --- a/src/audio.h +++ b/src/audio.h @@ -33,7 +33,7 @@ * quite make out their meaning yet) */ struct AudioPrivate; -struct Config; +struct RGSSThreadData; class Audio { @@ -70,7 +70,7 @@ public: void reset(); private: - Audio(const Config &conf); + Audio(RGSSThreadData &rtData); ~Audio(); friend struct SharedStatePrivate; diff --git a/src/eventthread.cpp b/src/eventthread.cpp index 64212b2..f87b5aa 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -76,6 +76,8 @@ void EventThread::process(RGSSThreadData &rtData) SDL_Window *win = rtData.window; UnidirMessage &windowSizeMsg = rtData.windowSizeMsg; + SDL_SetEventFilter(eventFilter, &rtData); + fullscreen = rtData.config.fullscreen; int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT; @@ -402,12 +404,63 @@ void EventThread::process(RGSSThreadData &rtData) break; } + /* Just in case */ + rtData.syncPoint.resumeThreads(); + if (SDL_JoystickGetAttached(js)) SDL_JoystickClose(js); delete sMenu; } +int EventThread::eventFilter(void *data, SDL_Event *event) +{ + RGSSThreadData &rtData = *static_cast(data); + + switch (event->type) + { + case SDL_APP_WILLENTERBACKGROUND : + Debug() << "SDL_APP_WILLENTERBACKGROUND"; + + rtData.syncPoint.haltThreads(); + + return 0; + + case SDL_APP_DIDENTERBACKGROUND : + Debug() << "SDL_APP_DIDENTERBACKGROUND"; + return 0; + + case SDL_APP_WILLENTERFOREGROUND : + Debug() << "SDL_APP_WILLENTERFOREGROUND"; + return 0; + + case SDL_APP_DIDENTERFOREGROUND : + Debug() << "SDL_APP_DIDENTERFOREGROUND"; + + rtData.syncPoint.resumeThreads(); + + return 0; + + case SDL_APP_TERMINATING : + Debug() << "SDL_APP_TERMINATING"; + return 0; + + case SDL_APP_LOWMEMORY : + Debug() << "SDL_APP_LOWMEMORY"; + return 0; + + case SDL_RENDER_TARGETS_RESET : + Debug() << "****** SDL_RENDER_TARGETS_RESET"; + return 0; + + case SDL_RENDER_DEVICE_RESET : + Debug() << "****** SDL_RENDER_DEVICE_RESET"; + return 0; + } + + return 1; +} + void EventThread::cleanup() { SDL_Event event; @@ -539,3 +592,87 @@ void EventThread::notifyFrame() event.user.type = usrIdStart + UPDATE_FPS; SDL_PushEvent(&event); } + +void SyncPoint::haltThreads() +{ + if (mainSync.locked) + return; + + /* Lock the reply sync first to avoid races */ + reply.lock(); + + /* Lock main sync and sleep until RGSS thread + * reports back */ + mainSync.lock(); + reply.waitForUnlock(); + + /* Now that the RGSS thread is asleep, we can + * safely put the other threads to sleep as well + * without causing deadlocks */ + secondSync.lock(); +} + +void SyncPoint::resumeThreads() +{ + if (!mainSync.locked) + return; + + mainSync.unlock(false); + secondSync.unlock(true); +} + +bool SyncPoint::mainSyncLocked() +{ + return mainSync.locked; +} + +void SyncPoint::waitMainSync() +{ + reply.unlock(false); + mainSync.waitForUnlock(); +} + +void SyncPoint::passSecondarySync() +{ + if (!secondSync.locked) + return; + + secondSync.waitForUnlock(); +} + +SyncPoint::Util::Util() +{ + mut = SDL_CreateMutex(); + cond = SDL_CreateCond(); +} + +SyncPoint::Util::~Util() +{ + SDL_DestroyCond(cond); + SDL_DestroyMutex(mut); +} + +void SyncPoint::Util::lock() +{ + locked.set(); +} + +void SyncPoint::Util::unlock(bool multi) +{ + locked.clear(); + + if (multi) + SDL_CondBroadcast(cond); + else + SDL_CondSignal(cond); +} + +void SyncPoint::Util::waitForUnlock() +{ + SDL_LockMutex(mut); + + while (locked) + SDL_CondWait(cond, mut); + + SDL_UnlockMutex(mut); +} diff --git a/src/eventthread.h b/src/eventthread.h index 9dcc21f..46051f1 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -39,6 +39,7 @@ struct RGSSThreadData; typedef struct ALCdevice_struct ALCdevice; struct SDL_Window; +union SDL_Event; #define MAX_FINGERS 4 @@ -98,6 +99,8 @@ public: void notifyFrame(); private: + static int eventFilter(void *, SDL_Event*); + void resetInputStates(); void setFullscreen(SDL_Window *, bool mode); void updateCursorState(bool inWindow); @@ -174,6 +177,39 @@ private: T current; }; +struct SyncPoint +{ + /* Used by eventFilter to control sleep/wakeup */ + void haltThreads(); + void resumeThreads(); + + /* Used by RGSS thread */ + bool mainSyncLocked(); + void waitMainSync(); + + /* Used by secondary (audio) threads */ + void passSecondarySync(); + +private: + struct Util + { + Util(); + ~Util(); + + void lock(); + void unlock(bool multi); + void waitForUnlock(); + + AtomicFlag locked; + SDL_mutex *mut; + SDL_cond *cond; + }; + + Util mainSync; + Util reply; + Util secondSync; +}; + struct RGSSThreadData { /* Main thread sets this to request RGSS thread to terminate */ @@ -191,6 +227,7 @@ struct RGSSThreadData EventThread *ethread; UnidirMessage windowSizeMsg; UnidirMessage bindingUpdateMsg; + SyncPoint syncPoint; const char *argv0; diff --git a/src/graphics.cpp b/src/graphics.cpp index 2f31077..a27914e 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -466,6 +466,7 @@ struct GraphicsPrivate ScreenScene screen; RGSSThreadData *threadData; + SDL_GLContext glCtx; int frameRate; int frameCount; @@ -489,6 +490,7 @@ struct GraphicsPrivate winSize(rtData->config.defScreenW, rtData->config.defScreenH), screen(scRes.x, scRes.y), threadData(rtData), + glCtx(SDL_GL_GetCurrentContext()), frameRate(DEF_FRAMERATE), frameCount(0), brightness(255), @@ -622,6 +624,21 @@ struct GraphicsPrivate swapGLBuffer(); } + + void checkSyncLock() + { + if (!threadData->syncPoint.mainSyncLocked()) + return; + + /* Releasing the GL context before sleeping and making it + * current again on wakeup seems to avoid the context loss + * when the app moves into the background on Android */ + SDL_GL_MakeCurrent(threadData->window, 0); + threadData->syncPoint.waitMainSync(); + SDL_GL_MakeCurrent(threadData->window, glCtx); + + fpsLimiter.resetFrameAdjust(); + } }; Graphics::Graphics(RGSSThreadData *data) @@ -651,6 +668,7 @@ Graphics::~Graphics() void Graphics::update() { p->checkShutDownReset(); + p->checkSyncLock(); if (p->frozen) return; @@ -692,6 +710,8 @@ void Graphics::transition(int duration, const char *filename, int vague) { + p->checkSyncLock(); + if (!p->frozen) return; @@ -752,6 +772,8 @@ void Graphics::transition(int duration, return; } + p->checkSyncLock(); + const float prog = i * (1.0 / duration); if (transMap) diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index ff0989b..b7a2c27 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -108,7 +108,7 @@ struct SharedStatePrivate midiState(threadData->config), graphics(threadData), input(*threadData), - audio(threadData->config), + audio(*threadData), fontState(threadData->config), stampCounter(0) {