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) {