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 "sharedstate.h"
|
||||||
#include "sharedmidistate.h"
|
#include "sharedmidistate.h"
|
||||||
|
#include "eventthread.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "aldatasource.h"
|
#include "aldatasource.h"
|
||||||
#include "fluid-fun.h"
|
#include "fluid-fun.h"
|
||||||
|
@ -361,6 +362,8 @@ void ALStream::streamData()
|
||||||
* refill and queue them up again */
|
* refill and queue them up again */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
shState->rtData().syncPoint.passSecondarySync();
|
||||||
|
|
||||||
ALint procBufs = AL::Source::getProcBufferCount(alSrc);
|
ALint procBufs = AL::Source::getProcBufferCount(alSrc);
|
||||||
|
|
||||||
while (procBufs--)
|
while (procBufs--)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "soundemitter.h"
|
#include "soundemitter.h"
|
||||||
#include "sharedstate.h"
|
#include "sharedstate.h"
|
||||||
#include "sharedmidistate.h"
|
#include "sharedmidistate.h"
|
||||||
|
#include "eventthread.h"
|
||||||
#include "sdl-util.h"
|
#include "sdl-util.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -40,6 +41,8 @@ struct AudioPrivate
|
||||||
|
|
||||||
SoundEmitter se;
|
SoundEmitter se;
|
||||||
|
|
||||||
|
SyncPoint &syncPoint;
|
||||||
|
|
||||||
/* The 'MeWatch' is responsible for detecting
|
/* The 'MeWatch' is responsible for detecting
|
||||||
* a playing ME, quickly fading out the BGM and
|
* a playing ME, quickly fading out the BGM and
|
||||||
* keeping it paused/stopped while the ME plays,
|
* keeping it paused/stopped while the ME plays,
|
||||||
|
@ -60,11 +63,12 @@ struct AudioPrivate
|
||||||
MeWatchState state;
|
MeWatchState state;
|
||||||
} meWatch;
|
} meWatch;
|
||||||
|
|
||||||
AudioPrivate(const Config &conf)
|
AudioPrivate(RGSSThreadData &rtData)
|
||||||
: bgm(ALStream::Looped, "bgm"),
|
: bgm(ALStream::Looped, "bgm"),
|
||||||
bgs(ALStream::Looped, "bgs"),
|
bgs(ALStream::Looped, "bgs"),
|
||||||
me(ALStream::NotLooped, "me"),
|
me(ALStream::NotLooped, "me"),
|
||||||
se(conf)
|
se(rtData.config),
|
||||||
|
syncPoint(rtData.syncPoint)
|
||||||
{
|
{
|
||||||
meWatch.state = MeNotPlaying;
|
meWatch.state = MeNotPlaying;
|
||||||
meWatch.thread = createSDLThread
|
meWatch.thread = createSDLThread
|
||||||
|
@ -84,6 +88,8 @@ struct AudioPrivate
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
syncPoint.passSecondarySync();
|
||||||
|
|
||||||
if (meWatch.termReq)
|
if (meWatch.termReq)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -231,8 +237,8 @@ struct AudioPrivate
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Audio::Audio(const Config &conf)
|
Audio::Audio(RGSSThreadData &rtData)
|
||||||
: p(new AudioPrivate(conf))
|
: p(new AudioPrivate(rtData))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
* quite make out their meaning yet) */
|
* quite make out their meaning yet) */
|
||||||
|
|
||||||
struct AudioPrivate;
|
struct AudioPrivate;
|
||||||
struct Config;
|
struct RGSSThreadData;
|
||||||
|
|
||||||
class Audio
|
class Audio
|
||||||
{
|
{
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Audio(const Config &conf);
|
Audio(RGSSThreadData &rtData);
|
||||||
~Audio();
|
~Audio();
|
||||||
|
|
||||||
friend struct SharedStatePrivate;
|
friend struct SharedStatePrivate;
|
||||||
|
|
|
@ -76,6 +76,8 @@ void EventThread::process(RGSSThreadData &rtData)
|
||||||
SDL_Window *win = rtData.window;
|
SDL_Window *win = rtData.window;
|
||||||
UnidirMessage<Vec2i> &windowSizeMsg = rtData.windowSizeMsg;
|
UnidirMessage<Vec2i> &windowSizeMsg = rtData.windowSizeMsg;
|
||||||
|
|
||||||
|
SDL_SetEventFilter(eventFilter, &rtData);
|
||||||
|
|
||||||
fullscreen = rtData.config.fullscreen;
|
fullscreen = rtData.config.fullscreen;
|
||||||
int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT;
|
int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT;
|
||||||
|
|
||||||
|
@ -402,12 +404,63 @@ void EventThread::process(RGSSThreadData &rtData)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Just in case */
|
||||||
|
rtData.syncPoint.resumeThreads();
|
||||||
|
|
||||||
if (SDL_JoystickGetAttached(js))
|
if (SDL_JoystickGetAttached(js))
|
||||||
SDL_JoystickClose(js);
|
SDL_JoystickClose(js);
|
||||||
|
|
||||||
delete sMenu;
|
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()
|
void EventThread::cleanup()
|
||||||
{
|
{
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
@ -539,3 +592,87 @@ void EventThread::notifyFrame()
|
||||||
event.user.type = usrIdStart + UPDATE_FPS;
|
event.user.type = usrIdStart + UPDATE_FPS;
|
||||||
SDL_PushEvent(&event);
|
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;
|
struct RGSSThreadData;
|
||||||
typedef struct ALCdevice_struct ALCdevice;
|
typedef struct ALCdevice_struct ALCdevice;
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
|
union SDL_Event;
|
||||||
|
|
||||||
#define MAX_FINGERS 4
|
#define MAX_FINGERS 4
|
||||||
|
|
||||||
|
@ -98,6 +99,8 @@ public:
|
||||||
void notifyFrame();
|
void notifyFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static int eventFilter(void *, SDL_Event*);
|
||||||
|
|
||||||
void resetInputStates();
|
void resetInputStates();
|
||||||
void setFullscreen(SDL_Window *, bool mode);
|
void setFullscreen(SDL_Window *, bool mode);
|
||||||
void updateCursorState(bool inWindow);
|
void updateCursorState(bool inWindow);
|
||||||
|
@ -174,6 +177,39 @@ private:
|
||||||
T current;
|
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
|
struct RGSSThreadData
|
||||||
{
|
{
|
||||||
/* Main thread sets this to request RGSS thread to terminate */
|
/* Main thread sets this to request RGSS thread to terminate */
|
||||||
|
@ -191,6 +227,7 @@ struct RGSSThreadData
|
||||||
EventThread *ethread;
|
EventThread *ethread;
|
||||||
UnidirMessage<Vec2i> windowSizeMsg;
|
UnidirMessage<Vec2i> windowSizeMsg;
|
||||||
UnidirMessage<BDescVec> bindingUpdateMsg;
|
UnidirMessage<BDescVec> bindingUpdateMsg;
|
||||||
|
SyncPoint syncPoint;
|
||||||
|
|
||||||
const char *argv0;
|
const char *argv0;
|
||||||
|
|
||||||
|
|
|
@ -466,6 +466,7 @@ struct GraphicsPrivate
|
||||||
|
|
||||||
ScreenScene screen;
|
ScreenScene screen;
|
||||||
RGSSThreadData *threadData;
|
RGSSThreadData *threadData;
|
||||||
|
SDL_GLContext glCtx;
|
||||||
|
|
||||||
int frameRate;
|
int frameRate;
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
@ -489,6 +490,7 @@ struct GraphicsPrivate
|
||||||
winSize(rtData->config.defScreenW, rtData->config.defScreenH),
|
winSize(rtData->config.defScreenW, rtData->config.defScreenH),
|
||||||
screen(scRes.x, scRes.y),
|
screen(scRes.x, scRes.y),
|
||||||
threadData(rtData),
|
threadData(rtData),
|
||||||
|
glCtx(SDL_GL_GetCurrentContext()),
|
||||||
frameRate(DEF_FRAMERATE),
|
frameRate(DEF_FRAMERATE),
|
||||||
frameCount(0),
|
frameCount(0),
|
||||||
brightness(255),
|
brightness(255),
|
||||||
|
@ -622,6 +624,21 @@ struct GraphicsPrivate
|
||||||
|
|
||||||
swapGLBuffer();
|
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)
|
Graphics::Graphics(RGSSThreadData *data)
|
||||||
|
@ -651,6 +668,7 @@ Graphics::~Graphics()
|
||||||
void Graphics::update()
|
void Graphics::update()
|
||||||
{
|
{
|
||||||
p->checkShutDownReset();
|
p->checkShutDownReset();
|
||||||
|
p->checkSyncLock();
|
||||||
|
|
||||||
if (p->frozen)
|
if (p->frozen)
|
||||||
return;
|
return;
|
||||||
|
@ -692,6 +710,8 @@ void Graphics::transition(int duration,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
int vague)
|
int vague)
|
||||||
{
|
{
|
||||||
|
p->checkSyncLock();
|
||||||
|
|
||||||
if (!p->frozen)
|
if (!p->frozen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -752,6 +772,8 @@ void Graphics::transition(int duration,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->checkSyncLock();
|
||||||
|
|
||||||
const float prog = i * (1.0 / duration);
|
const float prog = i * (1.0 / duration);
|
||||||
|
|
||||||
if (transMap)
|
if (transMap)
|
||||||
|
|
|
@ -108,7 +108,7 @@ struct SharedStatePrivate
|
||||||
midiState(threadData->config),
|
midiState(threadData->config),
|
||||||
graphics(threadData),
|
graphics(threadData),
|
||||||
input(*threadData),
|
input(*threadData),
|
||||||
audio(threadData->config),
|
audio(*threadData),
|
||||||
fontState(threadData->config),
|
fontState(threadData->config),
|
||||||
stampCounter(0)
|
stampCounter(0)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue