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.
This commit is contained in:
parent
012d87d05a
commit
7c6a2b2c62
|
@ -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--)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "soundemitter.h"
|
||||
#include "sharedstate.h"
|
||||
#include "sharedmidistate.h"
|
||||
#include "eventthread.h"
|
||||
#include "sdl-util.h"
|
||||
|
||||
#include <string>
|
||||
|
@ -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))
|
||||
{}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -76,6 +76,8 @@ void EventThread::process(RGSSThreadData &rtData)
|
|||
SDL_Window *win = rtData.window;
|
||||
UnidirMessage<Vec2i> &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<RGSSThreadData*>(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);
|
||||
}
|
||||
|
|
|
@ -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<Vec2i> windowSizeMsg;
|
||||
UnidirMessage<BDescVec> bindingUpdateMsg;
|
||||
SyncPoint syncPoint;
|
||||
|
||||
const char *argv0;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -108,7 +108,7 @@ struct SharedStatePrivate
|
|||
midiState(threadData->config),
|
||||
graphics(threadData),
|
||||
input(*threadData),
|
||||
audio(threadData->config),
|
||||
audio(*threadData),
|
||||
fontState(threadData->config),
|
||||
stampCounter(0)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue