mirror of https://github.com/mgba-emu/mgba.git
Core: Make threading optionally opaque
This commit is contained in:
parent
dc60c28bf1
commit
e1325b0373
|
@ -11,15 +11,40 @@
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
#include <mgba/core/log.h>
|
#include <mgba/core/log.h>
|
||||||
#include <mgba/core/rewind.h>
|
|
||||||
#include <mgba/core/sync.h>
|
|
||||||
#include <mgba-util/threading.h>
|
|
||||||
|
|
||||||
struct mCoreThread;
|
struct mCoreThread;
|
||||||
struct mCore;
|
struct mCore;
|
||||||
|
|
||||||
typedef void (*ThreadCallback)(struct mCoreThread* threadContext);
|
typedef void (*ThreadCallback)(struct mCoreThread* threadContext);
|
||||||
|
|
||||||
|
struct mCoreThread;
|
||||||
|
struct mThreadLogger {
|
||||||
|
struct mLogger d;
|
||||||
|
struct mCoreThread* p;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mCoreThreadInternal;
|
||||||
|
struct mCoreThread {
|
||||||
|
// Input
|
||||||
|
struct mCore* core;
|
||||||
|
|
||||||
|
struct mThreadLogger logger;
|
||||||
|
ThreadCallback startCallback;
|
||||||
|
ThreadCallback resetCallback;
|
||||||
|
ThreadCallback cleanCallback;
|
||||||
|
ThreadCallback frameCallback;
|
||||||
|
ThreadCallback sleepCallback;
|
||||||
|
void* userData;
|
||||||
|
void (*run)(struct mCoreThread*);
|
||||||
|
|
||||||
|
struct mCoreThreadInternal* impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef OPAQUE_THREADING
|
||||||
|
#include <mgba/core/rewind.h>
|
||||||
|
#include <mgba/core/sync.h>
|
||||||
|
#include <mgba-util/threading.h>
|
||||||
|
|
||||||
enum mCoreThreadState {
|
enum mCoreThreadState {
|
||||||
THREAD_INITIALIZED = -1,
|
THREAD_INITIALIZED = -1,
|
||||||
THREAD_RUNNING = 0,
|
THREAD_RUNNING = 0,
|
||||||
|
@ -37,17 +62,7 @@ enum mCoreThreadState {
|
||||||
THREAD_CRASHED
|
THREAD_CRASHED
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mCoreThread;
|
struct mCoreThreadInternal {
|
||||||
struct mThreadLogger {
|
|
||||||
struct mLogger d;
|
|
||||||
struct mCoreThread* p;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mCoreThread {
|
|
||||||
// Input
|
|
||||||
struct mCore* core;
|
|
||||||
|
|
||||||
// Threading state
|
|
||||||
Thread thread;
|
Thread thread;
|
||||||
enum mCoreThreadState state;
|
enum mCoreThreadState state;
|
||||||
|
|
||||||
|
@ -57,19 +72,12 @@ struct mCoreThread {
|
||||||
int interruptDepth;
|
int interruptDepth;
|
||||||
bool frameWasOn;
|
bool frameWasOn;
|
||||||
|
|
||||||
struct mThreadLogger logger;
|
|
||||||
ThreadCallback startCallback;
|
|
||||||
ThreadCallback resetCallback;
|
|
||||||
ThreadCallback cleanCallback;
|
|
||||||
ThreadCallback frameCallback;
|
|
||||||
ThreadCallback sleepCallback;
|
|
||||||
void* userData;
|
|
||||||
void (*run)(struct mCoreThread*);
|
|
||||||
|
|
||||||
struct mCoreSync sync;
|
struct mCoreSync sync;
|
||||||
struct mCoreRewindContext rewind;
|
struct mCoreRewindContext rewind;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
bool mCoreThreadStart(struct mCoreThread* threadContext);
|
bool mCoreThreadStart(struct mCoreThread* threadContext);
|
||||||
bool mCoreThreadHasStarted(struct mCoreThread* threadContext);
|
bool mCoreThreadHasStarted(struct mCoreThread* threadContext);
|
||||||
bool mCoreThreadHasExited(struct mCoreThread* threadContext);
|
bool mCoreThreadHasExited(struct mCoreThread* threadContext);
|
||||||
|
|
|
@ -38,7 +38,7 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
|
||||||
|
|
||||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
|
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
|
||||||
|
|
||||||
static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
|
static void _changeState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState newState, bool broadcast) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->stateMutex);
|
||||||
threadContext->state = newState;
|
threadContext->state = newState;
|
||||||
if (broadcast) {
|
if (broadcast) {
|
||||||
|
@ -47,13 +47,13 @@ static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadStat
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _waitOnInterrupt(struct mCoreThread* threadContext) {
|
static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) {
|
||||||
while (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
while (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
||||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThreadState oldState) {
|
static void _waitUntilNotState(struct mCoreThreadInternal* threadContext, enum mCoreThreadState oldState) {
|
||||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
MutexLock(&threadContext->sync.videoFrameMutex);
|
||||||
bool videoFrameWait = threadContext->sync.videoFrameWait;
|
bool videoFrameWait = threadContext->sync.videoFrameWait;
|
||||||
threadContext->sync.videoFrameWait = false;
|
threadContext->sync.videoFrameWait = false;
|
||||||
|
@ -81,7 +81,7 @@ static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThre
|
||||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _pauseThread(struct mCoreThread* threadContext) {
|
static void _pauseThread(struct mCoreThreadInternal* threadContext) {
|
||||||
threadContext->state = THREAD_PAUSING;
|
threadContext->state = THREAD_PAUSING;
|
||||||
_waitUntilNotState(threadContext, THREAD_PAUSING);
|
_waitUntilNotState(threadContext, THREAD_PAUSING);
|
||||||
}
|
}
|
||||||
|
@ -92,11 +92,11 @@ void _frameStarted(void* context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
|
if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
|
||||||
if (thread->state != THREAD_REWINDING) {
|
if (thread->impl->state != THREAD_REWINDING) {
|
||||||
mCoreRewindAppend(&thread->rewind, thread->core);
|
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
||||||
} else if (thread->state == THREAD_REWINDING) {
|
} else if (thread->impl->state == THREAD_REWINDING) {
|
||||||
if (!mCoreRewindRestore(&thread->rewind, thread->core)) {
|
if (!mCoreRewindRestore(&thread->impl->rewind, thread->core)) {
|
||||||
mCoreRewindAppend(&thread->rewind, thread->core);
|
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ void _crashed(void* context) {
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_changeState(thread, THREAD_CRASHED, true);
|
_changeState(thread->impl, THREAD_CRASHED, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _coreSleep(void* context) {
|
void _coreSleep(void* context) {
|
||||||
|
@ -157,7 +157,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
.context = threadContext
|
.context = threadContext
|
||||||
};
|
};
|
||||||
core->addCoreCallbacks(core, &callbacks);
|
core->addCoreCallbacks(core, &callbacks);
|
||||||
core->setSync(core, &threadContext->sync);
|
core->setSync(core, &threadContext->impl->sync);
|
||||||
core->reset(core);
|
core->reset(core);
|
||||||
|
|
||||||
struct mLogFilter filter;
|
struct mLogFilter filter;
|
||||||
|
@ -168,11 +168,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
|
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
|
||||||
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity, true);
|
mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
|
||||||
threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
|
threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_changeState(threadContext, THREAD_RUNNING, true);
|
_changeState(threadContext->impl, THREAD_RUNNING, true);
|
||||||
|
|
||||||
if (threadContext->startCallback) {
|
if (threadContext->startCallback) {
|
||||||
threadContext->startCallback(threadContext);
|
threadContext->startCallback(threadContext);
|
||||||
|
@ -181,49 +181,50 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
threadContext->resetCallback(threadContext);
|
threadContext->resetCallback(threadContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (threadContext->state < THREAD_EXITING) {
|
struct mCoreThreadInternal* impl = threadContext->impl;
|
||||||
|
while (impl->state < THREAD_EXITING) {
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
struct mDebugger* debugger = core->debugger;
|
struct mDebugger* debugger = core->debugger;
|
||||||
if (debugger) {
|
if (debugger) {
|
||||||
mDebuggerRun(debugger);
|
mDebuggerRun(debugger);
|
||||||
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
||||||
_changeState(threadContext, THREAD_EXITING, false);
|
_changeState(impl, THREAD_EXITING, false);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
while (threadContext->state <= THREAD_MAX_RUNNING) {
|
while (impl->state <= THREAD_MAX_RUNNING) {
|
||||||
core->runLoop(core);
|
core->runLoop(core);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int resetScheduled = 0;
|
int resetScheduled = 0;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&impl->stateMutex);
|
||||||
while (threadContext->state > THREAD_MAX_RUNNING && threadContext->state < THREAD_EXITING) {
|
while (impl->state > THREAD_MAX_RUNNING && impl->state < THREAD_EXITING) {
|
||||||
if (threadContext->state == THREAD_PAUSING) {
|
if (impl->state == THREAD_PAUSING) {
|
||||||
threadContext->state = THREAD_PAUSED;
|
impl->state = THREAD_PAUSED;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&impl->stateCond);
|
||||||
}
|
}
|
||||||
if (threadContext->state == THREAD_INTERRUPTING) {
|
if (impl->state == THREAD_INTERRUPTING) {
|
||||||
threadContext->state = THREAD_INTERRUPTED;
|
impl->state = THREAD_INTERRUPTED;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&impl->stateCond);
|
||||||
}
|
}
|
||||||
if (threadContext->state == THREAD_RUN_ON) {
|
if (impl->state == THREAD_RUN_ON) {
|
||||||
if (threadContext->run) {
|
if (threadContext->run) {
|
||||||
threadContext->run(threadContext);
|
threadContext->run(threadContext);
|
||||||
}
|
}
|
||||||
threadContext->state = threadContext->savedState;
|
impl->state = impl->savedState;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&impl->stateCond);
|
||||||
}
|
}
|
||||||
if (threadContext->state == THREAD_RESETING) {
|
if (impl->state == THREAD_RESETING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
impl->state = THREAD_RUNNING;
|
||||||
resetScheduled = 1;
|
resetScheduled = 1;
|
||||||
}
|
}
|
||||||
while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_WAITING) {
|
while (impl->state == THREAD_PAUSED || impl->state == THREAD_INTERRUPTED || impl->state == THREAD_WAITING) {
|
||||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
ConditionWait(&impl->stateCond, &impl->stateMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&impl->stateMutex);
|
||||||
if (resetScheduled) {
|
if (resetScheduled) {
|
||||||
core->reset(core);
|
core->reset(core);
|
||||||
if (threadContext->resetCallback) {
|
if (threadContext->resetCallback) {
|
||||||
|
@ -232,12 +233,12 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (threadContext->state < THREAD_SHUTDOWN) {
|
while (impl->state < THREAD_SHUTDOWN) {
|
||||||
_changeState(threadContext, THREAD_SHUTDOWN, false);
|
_changeState(impl, THREAD_SHUTDOWN, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core->opts.rewindEnable) {
|
if (core->opts.rewindEnable) {
|
||||||
mCoreRewindContextDeinit(&threadContext->rewind);
|
mCoreRewindContextDeinit(&impl->rewind);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threadContext->cleanCallback) {
|
if (threadContext->cleanCallback) {
|
||||||
|
@ -251,27 +252,28 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadStart(struct mCoreThread* threadContext) {
|
bool mCoreThreadStart(struct mCoreThread* threadContext) {
|
||||||
threadContext->state = THREAD_INITIALIZED;
|
threadContext->impl = malloc(sizeof(*threadContext->impl));
|
||||||
|
threadContext->impl->state = THREAD_INITIALIZED;
|
||||||
threadContext->logger.p = threadContext;
|
threadContext->logger.p = threadContext;
|
||||||
if (!threadContext->logger.d.log) {
|
if (!threadContext->logger.d.log) {
|
||||||
threadContext->logger.d.log = _mCoreLog;
|
threadContext->logger.d.log = _mCoreLog;
|
||||||
threadContext->logger.d.filter = NULL;
|
threadContext->logger.d.filter = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!threadContext->sync.fpsTarget) {
|
if (!threadContext->impl->sync.fpsTarget) {
|
||||||
threadContext->sync.fpsTarget = _defaultFPSTarget;
|
threadContext->impl->sync.fpsTarget = _defaultFPSTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexInit(&threadContext->stateMutex);
|
MutexInit(&threadContext->impl->stateMutex);
|
||||||
ConditionInit(&threadContext->stateCond);
|
ConditionInit(&threadContext->impl->stateCond);
|
||||||
|
|
||||||
MutexInit(&threadContext->sync.videoFrameMutex);
|
MutexInit(&threadContext->impl->sync.videoFrameMutex);
|
||||||
ConditionInit(&threadContext->sync.videoFrameAvailableCond);
|
ConditionInit(&threadContext->impl->sync.videoFrameAvailableCond);
|
||||||
ConditionInit(&threadContext->sync.videoFrameRequiredCond);
|
ConditionInit(&threadContext->impl->sync.videoFrameRequiredCond);
|
||||||
MutexInit(&threadContext->sync.audioBufferMutex);
|
MutexInit(&threadContext->impl->sync.audioBufferMutex);
|
||||||
ConditionInit(&threadContext->sync.audioRequiredCond);
|
ConditionInit(&threadContext->impl->sync.audioRequiredCond);
|
||||||
|
|
||||||
threadContext->interruptDepth = 0;
|
threadContext->impl->interruptDepth = 0;
|
||||||
|
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
sigset_t signals;
|
sigset_t signals;
|
||||||
|
@ -281,271 +283,289 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) {
|
||||||
pthread_sigmask(SIG_BLOCK, &signals, 0);
|
pthread_sigmask(SIG_BLOCK, &signals, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
threadContext->sync.audioWait = threadContext->core->opts.audioSync;
|
threadContext->impl->sync.audioWait = threadContext->core->opts.audioSync;
|
||||||
threadContext->sync.videoFrameWait = threadContext->core->opts.videoSync;
|
threadContext->impl->sync.videoFrameWait = threadContext->core->opts.videoSync;
|
||||||
threadContext->sync.fpsTarget = threadContext->core->opts.fpsTarget;
|
threadContext->impl->sync.fpsTarget = threadContext->core->opts.fpsTarget;
|
||||||
|
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
|
ThreadCreate(&threadContext->impl->thread, _mCoreThreadRun, threadContext);
|
||||||
while (threadContext->state < THREAD_RUNNING) {
|
while (threadContext->impl->state < THREAD_RUNNING) {
|
||||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
ConditionWait(&threadContext->impl->stateCond, &threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
|
bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
|
||||||
|
if (!threadContext->impl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool hasStarted;
|
bool hasStarted;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
hasStarted = threadContext->state > THREAD_INITIALIZED;
|
hasStarted = threadContext->impl->state > THREAD_INITIALIZED;
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return hasStarted;
|
return hasStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
|
bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
|
||||||
|
if (!threadContext->impl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool hasExited;
|
bool hasExited;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
hasExited = threadContext->state > THREAD_EXITING;
|
hasExited = threadContext->impl->state > THREAD_EXITING;
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return hasExited;
|
return hasExited;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
|
bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
|
||||||
|
if (!threadContext->impl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool hasExited;
|
bool hasExited;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
hasExited = threadContext->state == THREAD_CRASHED;
|
hasExited = threadContext->impl->state == THREAD_CRASHED;
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return hasExited;
|
return hasExited;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
|
void mCoreThreadMarkCrashed(struct mCoreThread* threadContext) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
threadContext->state = THREAD_CRASHED;
|
threadContext->impl->state = THREAD_CRASHED;
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadEnd(struct mCoreThread* threadContext) {
|
void mCoreThreadEnd(struct mCoreThread* threadContext) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
threadContext->state = THREAD_EXITING;
|
threadContext->impl->state = THREAD_EXITING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
MutexLock(&threadContext->sync.audioBufferMutex);
|
MutexLock(&threadContext->impl->sync.audioBufferMutex);
|
||||||
threadContext->sync.audioWait = 0;
|
threadContext->impl->sync.audioWait = 0;
|
||||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
ConditionWake(&threadContext->impl->sync.audioRequiredCond);
|
||||||
MutexUnlock(&threadContext->sync.audioBufferMutex);
|
MutexUnlock(&threadContext->impl->sync.audioBufferMutex);
|
||||||
|
|
||||||
MutexLock(&threadContext->sync.videoFrameMutex);
|
MutexLock(&threadContext->impl->sync.videoFrameMutex);
|
||||||
threadContext->sync.videoFrameWait = false;
|
threadContext->impl->sync.videoFrameWait = false;
|
||||||
threadContext->sync.videoFrameOn = false;
|
threadContext->impl->sync.videoFrameOn = false;
|
||||||
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
|
ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond);
|
||||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond);
|
||||||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
MutexUnlock(&threadContext->impl->sync.videoFrameMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadReset(struct mCoreThread* threadContext) {
|
void mCoreThreadReset(struct mCoreThread* threadContext) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
if (threadContext->impl->state == THREAD_INTERRUPTED || threadContext->impl->state == THREAD_INTERRUPTING) {
|
||||||
threadContext->savedState = THREAD_RESETING;
|
threadContext->impl->savedState = THREAD_RESETING;
|
||||||
} else {
|
} else {
|
||||||
threadContext->state = THREAD_RESETING;
|
threadContext->impl->state = THREAD_RESETING;
|
||||||
}
|
}
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadJoin(struct mCoreThread* threadContext) {
|
void mCoreThreadJoin(struct mCoreThread* threadContext) {
|
||||||
ThreadJoin(threadContext->thread);
|
if (!threadContext->impl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ThreadJoin(threadContext->impl->thread);
|
||||||
|
|
||||||
MutexDeinit(&threadContext->stateMutex);
|
MutexDeinit(&threadContext->impl->stateMutex);
|
||||||
ConditionDeinit(&threadContext->stateCond);
|
ConditionDeinit(&threadContext->impl->stateCond);
|
||||||
|
|
||||||
MutexDeinit(&threadContext->sync.videoFrameMutex);
|
MutexDeinit(&threadContext->impl->sync.videoFrameMutex);
|
||||||
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
|
ConditionWake(&threadContext->impl->sync.videoFrameAvailableCond);
|
||||||
ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
|
ConditionDeinit(&threadContext->impl->sync.videoFrameAvailableCond);
|
||||||
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
|
ConditionWake(&threadContext->impl->sync.videoFrameRequiredCond);
|
||||||
ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
|
ConditionDeinit(&threadContext->impl->sync.videoFrameRequiredCond);
|
||||||
|
|
||||||
ConditionWake(&threadContext->sync.audioRequiredCond);
|
ConditionWake(&threadContext->impl->sync.audioRequiredCond);
|
||||||
ConditionDeinit(&threadContext->sync.audioRequiredCond);
|
ConditionDeinit(&threadContext->impl->sync.audioRequiredCond);
|
||||||
MutexDeinit(&threadContext->sync.audioBufferMutex);
|
MutexDeinit(&threadContext->impl->sync.audioBufferMutex);
|
||||||
|
|
||||||
|
free(threadContext->impl);
|
||||||
|
threadContext->impl = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
|
bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
|
||||||
return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
|
if (!threadContext->impl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return threadContext->impl->state >= THREAD_RUNNING && threadContext->impl->state < THREAD_EXITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
|
void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
|
||||||
if (!threadContext) {
|
if (!threadContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
++threadContext->interruptDepth;
|
++threadContext->impl->interruptDepth;
|
||||||
if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
|
if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
threadContext->savedState = threadContext->state;
|
threadContext->impl->savedState = threadContext->impl->state;
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
threadContext->state = THREAD_INTERRUPTING;
|
threadContext->impl->state = THREAD_INTERRUPTING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
|
_waitUntilNotState(threadContext->impl, THREAD_INTERRUPTING);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
|
void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
|
||||||
if (!threadContext) {
|
if (!threadContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
++threadContext->interruptDepth;
|
++threadContext->impl->interruptDepth;
|
||||||
if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
|
if (threadContext->impl->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
|
||||||
if (threadContext->state == THREAD_INTERRUPTING) {
|
if (threadContext->impl->state == THREAD_INTERRUPTING) {
|
||||||
threadContext->state = THREAD_INTERRUPTED;
|
threadContext->impl->state = THREAD_INTERRUPTED;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
threadContext->savedState = threadContext->state;
|
threadContext->impl->savedState = threadContext->impl->state;
|
||||||
threadContext->state = THREAD_INTERRUPTED;
|
threadContext->impl->state = THREAD_INTERRUPTING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadContinue(struct mCoreThread* threadContext) {
|
void mCoreThreadContinue(struct mCoreThread* threadContext) {
|
||||||
if (!threadContext) {
|
if (!threadContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
--threadContext->interruptDepth;
|
--threadContext->impl->interruptDepth;
|
||||||
if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
|
if (threadContext->impl->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
|
||||||
threadContext->state = threadContext->savedState;
|
threadContext->impl->state = threadContext->impl->savedState;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
|
void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
threadContext->run = run;
|
threadContext->run = run;
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
threadContext->savedState = threadContext->state;
|
threadContext->impl->savedState = threadContext->impl->state;
|
||||||
threadContext->state = THREAD_RUN_ON;
|
threadContext->impl->state = THREAD_RUN_ON;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
_waitUntilNotState(threadContext, THREAD_RUN_ON);
|
_waitUntilNotState(threadContext->impl, THREAD_RUN_ON);
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadPause(struct mCoreThread* threadContext) {
|
void mCoreThreadPause(struct mCoreThread* threadContext) {
|
||||||
bool frameOn = threadContext->sync.videoFrameOn;
|
bool frameOn = threadContext->impl->sync.videoFrameOn;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
if (threadContext->state == THREAD_RUNNING) {
|
if (threadContext->impl->state == THREAD_RUNNING) {
|
||||||
_pauseThread(threadContext);
|
_pauseThread(threadContext->impl);
|
||||||
threadContext->frameWasOn = frameOn;
|
threadContext->impl->frameWasOn = frameOn;
|
||||||
frameOn = false;
|
frameOn = false;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
|
|
||||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadUnpause(struct mCoreThread* threadContext) {
|
void mCoreThreadUnpause(struct mCoreThread* threadContext) {
|
||||||
bool frameOn = threadContext->sync.videoFrameOn;
|
bool frameOn = threadContext->impl->sync.videoFrameOn;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
threadContext->impl->state = THREAD_RUNNING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
frameOn = threadContext->frameWasOn;
|
frameOn = threadContext->impl->frameWasOn;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
|
|
||||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
|
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
|
||||||
bool isPaused;
|
bool isPaused;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (threadContext->interruptDepth) {
|
if (threadContext->impl->interruptDepth) {
|
||||||
isPaused = threadContext->savedState == THREAD_PAUSED;
|
isPaused = threadContext->impl->savedState == THREAD_PAUSED;
|
||||||
} else {
|
} else {
|
||||||
isPaused = threadContext->state == THREAD_PAUSED;
|
isPaused = threadContext->impl->state == THREAD_PAUSED;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return isPaused;
|
return isPaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
|
void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
|
||||||
bool frameOn = threadContext->sync.videoFrameOn;
|
bool frameOn = threadContext->impl->sync.videoFrameOn;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
|
if (threadContext->impl->state == THREAD_PAUSED || threadContext->impl->state == THREAD_PAUSING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
threadContext->impl->state = THREAD_RUNNING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
frameOn = threadContext->frameWasOn;
|
frameOn = threadContext->impl->frameWasOn;
|
||||||
} else if (threadContext->state == THREAD_RUNNING) {
|
} else if (threadContext->impl->state == THREAD_RUNNING) {
|
||||||
_pauseThread(threadContext);
|
_pauseThread(threadContext->impl);
|
||||||
threadContext->frameWasOn = frameOn;
|
threadContext->impl->frameWasOn = frameOn;
|
||||||
frameOn = false;
|
frameOn = false;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
|
|
||||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
|
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
|
||||||
bool frameOn = true;
|
bool frameOn = true;
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (threadContext->state == THREAD_RUNNING || (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING)) {
|
if (threadContext->impl->state == THREAD_RUNNING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING)) {
|
||||||
threadContext->state = THREAD_PAUSING;
|
threadContext->impl->state = THREAD_PAUSING;
|
||||||
frameOn = false;
|
frameOn = false;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
|
|
||||||
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
|
mCoreSyncSetVideoSync(&threadContext->impl->sync, frameOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
|
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->interruptDepth && threadContext->savedState == THREAD_REWINDING))) {
|
if (rewinding && (threadContext->impl->state == THREAD_REWINDING || (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_REWINDING))) {
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!rewinding && ((!threadContext->interruptDepth && threadContext->state != THREAD_REWINDING) || (threadContext->interruptDepth && threadContext->savedState != THREAD_REWINDING))) {
|
if (!rewinding && ((!threadContext->impl->interruptDepth && threadContext->impl->state != THREAD_REWINDING) || (threadContext->impl->interruptDepth && threadContext->impl->savedState != THREAD_REWINDING))) {
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_waitOnInterrupt(threadContext);
|
_waitOnInterrupt(threadContext->impl);
|
||||||
if (rewinding && threadContext->state == THREAD_RUNNING) {
|
if (rewinding && threadContext->impl->state == THREAD_RUNNING) {
|
||||||
threadContext->state = THREAD_REWINDING;
|
threadContext->impl->state = THREAD_REWINDING;
|
||||||
}
|
}
|
||||||
if (!rewinding && threadContext->state == THREAD_REWINDING) {
|
if (!rewinding && threadContext->impl->state == THREAD_REWINDING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
threadContext->impl->state = THREAD_RUNNING;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
|
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING) {
|
if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING) {
|
||||||
threadContext->savedState = THREAD_WAITING;
|
threadContext->impl->savedState = THREAD_WAITING;
|
||||||
} else if (threadContext->state == THREAD_RUNNING) {
|
} else if (threadContext->impl->state == THREAD_RUNNING) {
|
||||||
threadContext->state = THREAD_WAITING;
|
threadContext->impl->state = THREAD_WAITING;
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
|
void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
|
||||||
MutexLock(&threadContext->stateMutex);
|
MutexLock(&threadContext->impl->stateMutex);
|
||||||
if (threadContext->interruptDepth && threadContext->savedState == THREAD_WAITING) {
|
if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_WAITING) {
|
||||||
threadContext->savedState = THREAD_RUNNING;
|
threadContext->impl->savedState = THREAD_RUNNING;
|
||||||
} else if (threadContext->state == THREAD_WAITING) {
|
} else if (threadContext->impl->state == THREAD_WAITING) {
|
||||||
threadContext->state = THREAD_RUNNING;
|
threadContext->impl->state = THREAD_RUNNING;
|
||||||
ConditionWake(&threadContext->stateCond);
|
ConditionWake(&threadContext->impl->stateCond);
|
||||||
}
|
}
|
||||||
MutexUnlock(&threadContext->stateMutex);
|
MutexUnlock(&threadContext->impl->stateMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
|
|
|
@ -26,13 +26,13 @@ void AudioDevice::setFormat(const QAudioFormat& format) {
|
||||||
LOG(QT, INFO) << tr("Can't set format of context-less audio device");
|
LOG(QT, INFO) << tr("Can't set format of context-less audio device");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double fauxClock = GBAAudioCalculateRatio(1, m_context->sync.fpsTarget, 1);
|
double fauxClock = GBAAudioCalculateRatio(1, m_context->impl->sync.fpsTarget, 1);
|
||||||
mCoreSyncLockAudio(&m_context->sync);
|
mCoreSyncLockAudio(&m_context->impl->sync);
|
||||||
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 0),
|
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 0),
|
||||||
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock);
|
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock);
|
||||||
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 1),
|
blip_set_rates(m_context->core->getAudioChannel(m_context->core, 1),
|
||||||
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock);
|
m_context->core->frequency(m_context->core), format.sampleRate() * fauxClock);
|
||||||
mCoreSyncUnlockAudio(&m_context->sync);
|
mCoreSyncUnlockAudio(&m_context->impl->sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDevice::setInput(mCoreThread* input) {
|
void AudioDevice::setInput(mCoreThread* input) {
|
||||||
|
@ -49,14 +49,14 @@ qint64 AudioDevice::readData(char* data, qint64 maxSize) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCoreSyncLockAudio(&m_context->sync);
|
mCoreSyncLockAudio(&m_context->impl->sync);
|
||||||
int available = blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0));
|
int available = blip_samples_avail(m_context->core->getAudioChannel(m_context->core, 0));
|
||||||
if (available > maxSize / sizeof(GBAStereoSample)) {
|
if (available > maxSize / sizeof(GBAStereoSample)) {
|
||||||
available = maxSize / sizeof(GBAStereoSample);
|
available = maxSize / sizeof(GBAStereoSample);
|
||||||
}
|
}
|
||||||
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast<GBAStereoSample*>(data)->left, available, true);
|
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 0), &reinterpret_cast<GBAStereoSample*>(data)->left, available, true);
|
||||||
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast<GBAStereoSample*>(data)->right, available, true);
|
blip_read_samples(m_context->core->getAudioChannel(m_context->core, 1), &reinterpret_cast<GBAStereoSample*>(data)->right, available, true);
|
||||||
mCoreSyncConsumeAudio(&m_context->sync);
|
mCoreSyncConsumeAudio(&m_context->impl->sync);
|
||||||
return available * sizeof(GBAStereoSample);
|
return available * sizeof(GBAStereoSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
|
||||||
m_painter->moveToThread(m_drawThread);
|
m_painter->moveToThread(m_drawThread);
|
||||||
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
||||||
m_drawThread->start();
|
m_drawThread->start();
|
||||||
mCoreSyncSetVideoSync(&m_context->sync, false);
|
mCoreSyncSetVideoSync(&m_context->impl->sync, false);
|
||||||
|
|
||||||
lockAspectRatio(isAspectRatioLocked());
|
lockAspectRatio(isAspectRatioLocked());
|
||||||
lockIntegerScaling(isIntegerScalingLocked());
|
lockIntegerScaling(isIntegerScalingLocked());
|
||||||
|
@ -333,9 +333,9 @@ void PainterGL::draw() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) {
|
if (mCoreSyncWaitFrameStart(&m_context->impl->sync) || !m_queue.isEmpty()) {
|
||||||
dequeue();
|
dequeue();
|
||||||
mCoreSyncWaitFrameEnd(&m_context->sync);
|
mCoreSyncWaitFrameEnd(&m_context->impl->sync);
|
||||||
m_painter.begin(m_gl->context()->device());
|
m_painter.begin(m_gl->context()->device());
|
||||||
performDraw();
|
performDraw();
|
||||||
m_painter.end();
|
m_painter.end();
|
||||||
|
@ -349,7 +349,7 @@ void PainterGL::draw() {
|
||||||
m_delayTimer.restart();
|
m_delayTimer.restart();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mCoreSyncWaitFrameEnd(&m_context->sync);
|
mCoreSyncWaitFrameEnd(&m_context->impl->sync);
|
||||||
}
|
}
|
||||||
if (!m_queue.isEmpty()) {
|
if (!m_queue.isEmpty()) {
|
||||||
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
|
||||||
|
|
|
@ -79,7 +79,7 @@ GameController::GameController(QObject* parent)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
controller->m_fpsTarget = context->sync.fpsTarget;
|
controller->m_fpsTarget = context->impl->sync.fpsTarget;
|
||||||
|
|
||||||
if (controller->m_override) {
|
if (controller->m_override) {
|
||||||
controller->m_override->identify(context->core);
|
controller->m_override->identify(context->core);
|
||||||
|
@ -299,8 +299,8 @@ void GameController::setConfig(const mCoreConfig* config) {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
mCoreLoadForeignConfig(m_threadContext.core, config);
|
mCoreLoadForeignConfig(m_threadContext.core, config);
|
||||||
m_audioSync = m_threadContext.sync.audioWait;
|
m_audioSync = m_threadContext.impl->sync.audioWait;
|
||||||
m_videoSync = m_threadContext.sync.videoFrameWait;
|
m_videoSync = m_threadContext.impl->sync.videoFrameWait;
|
||||||
m_audioProcessor->setInput(&m_threadContext);
|
m_audioProcessor->setInput(&m_threadContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,13 +410,6 @@ void GameController::openGame(bool biosOnly) {
|
||||||
|
|
||||||
m_pauseAfterFrame = false;
|
m_pauseAfterFrame = false;
|
||||||
|
|
||||||
if (m_turbo) {
|
|
||||||
m_threadContext.sync.videoFrameWait = false;
|
|
||||||
m_threadContext.sync.audioWait = false;
|
|
||||||
} else {
|
|
||||||
m_threadContext.sync.videoFrameWait = m_videoSync;
|
|
||||||
m_threadContext.sync.audioWait = m_audioSync;
|
|
||||||
}
|
|
||||||
m_threadContext.core->init(m_threadContext.core);
|
m_threadContext.core->init(m_threadContext.core);
|
||||||
mCoreInitConfig(m_threadContext.core, nullptr);
|
mCoreInitConfig(m_threadContext.core, nullptr);
|
||||||
|
|
||||||
|
@ -483,6 +476,13 @@ void GameController::openGame(bool biosOnly) {
|
||||||
if (!mCoreThreadStart(&m_threadContext)) {
|
if (!mCoreThreadStart(&m_threadContext)) {
|
||||||
emit gameFailed();
|
emit gameFailed();
|
||||||
}
|
}
|
||||||
|
if (m_turbo) {
|
||||||
|
m_threadContext.impl->sync.videoFrameWait = false;
|
||||||
|
m_threadContext.impl->sync.audioWait = false;
|
||||||
|
} else {
|
||||||
|
m_threadContext.impl->sync.videoFrameWait = m_videoSync;
|
||||||
|
m_threadContext.impl->sync.audioWait = m_audioSync;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::loadBIOS(int platform, const QString& path) {
|
void GameController::loadBIOS(int platform, const QString& path) {
|
||||||
|
@ -704,14 +704,14 @@ void GameController::setRewind(bool enable, int capacity, bool rewindSave) {
|
||||||
if (m_gameOpen) {
|
if (m_gameOpen) {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) {
|
if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) {
|
||||||
mCoreRewindContextDeinit(&m_threadContext.rewind);
|
mCoreRewindContextDeinit(&m_threadContext.impl->rewind);
|
||||||
}
|
}
|
||||||
m_threadContext.core->opts.rewindEnable = enable;
|
m_threadContext.core->opts.rewindEnable = enable;
|
||||||
m_threadContext.core->opts.rewindBufferCapacity = capacity;
|
m_threadContext.core->opts.rewindBufferCapacity = capacity;
|
||||||
m_threadContext.core->opts.rewindSave = rewindSave;
|
m_threadContext.core->opts.rewindSave = rewindSave;
|
||||||
if (enable && capacity > 0) {
|
if (enable && capacity > 0) {
|
||||||
mCoreRewindContextInit(&m_threadContext.rewind, capacity, true);
|
mCoreRewindContextInit(&m_threadContext.impl->rewind, capacity, true);
|
||||||
m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0;
|
m_threadContext.impl->rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,7 +722,7 @@ void GameController::rewind(int states) {
|
||||||
states = INT_MAX;
|
states = INT_MAX;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < states; ++i) {
|
for (int i = 0; i < states; ++i) {
|
||||||
if (!mCoreRewindRestore(&m_threadContext.rewind, m_threadContext.core)) {
|
if (!mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -850,8 +850,10 @@ void GameController::startAudio() {
|
||||||
// Don't freeze!
|
// Don't freeze!
|
||||||
m_audioSync = false;
|
m_audioSync = false;
|
||||||
m_videoSync = true;
|
m_videoSync = true;
|
||||||
m_threadContext.sync.audioWait = false;
|
if (isLoaded()) {
|
||||||
m_threadContext.sync.videoFrameWait = true;
|
m_threadContext.impl->sync.audioWait = false;
|
||||||
|
m_threadContext.impl->sync.videoFrameWait = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,9 +874,11 @@ void GameController::setVideoLayerEnabled(int layer, bool enable) {
|
||||||
void GameController::setFPSTarget(float fps) {
|
void GameController::setFPSTarget(float fps) {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
m_fpsTarget = fps;
|
m_fpsTarget = fps;
|
||||||
m_threadContext.sync.fpsTarget = fps;
|
if (isLoaded()) {
|
||||||
if (m_turbo && m_turboSpeed > 0) {
|
m_threadContext.impl->sync.fpsTarget = fps;
|
||||||
m_threadContext.sync.fpsTarget *= m_turboSpeed;
|
if (m_turbo && m_turboSpeed > 0) {
|
||||||
|
m_threadContext.impl->sync.fpsTarget *= m_turboSpeed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_audioProcessor) {
|
if (m_audioProcessor) {
|
||||||
redoSamples(m_audioProcessor->getBufferSamples());
|
redoSamples(m_audioProcessor->getBufferSamples());
|
||||||
|
@ -992,22 +996,25 @@ void GameController::setTurboSpeed(float ratio) {
|
||||||
|
|
||||||
void GameController::enableTurbo() {
|
void GameController::enableTurbo() {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
|
if (!isLoaded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
bool shouldRedoSamples = false;
|
bool shouldRedoSamples = false;
|
||||||
if (!m_turbo) {
|
if (!m_turbo) {
|
||||||
shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget;
|
shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget;
|
||||||
m_threadContext.sync.fpsTarget = m_fpsTarget;
|
m_threadContext.impl->sync.fpsTarget = m_fpsTarget;
|
||||||
m_threadContext.sync.audioWait = m_audioSync;
|
m_threadContext.impl->sync.audioWait = m_audioSync;
|
||||||
m_threadContext.sync.videoFrameWait = m_videoSync;
|
m_threadContext.impl->sync.videoFrameWait = m_videoSync;
|
||||||
} else if (m_turboSpeed <= 0) {
|
} else if (m_turboSpeed <= 0) {
|
||||||
shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget;
|
shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget;
|
||||||
m_threadContext.sync.fpsTarget = m_fpsTarget;
|
m_threadContext.impl->sync.fpsTarget = m_fpsTarget;
|
||||||
m_threadContext.sync.audioWait = false;
|
m_threadContext.impl->sync.audioWait = false;
|
||||||
m_threadContext.sync.videoFrameWait = false;
|
m_threadContext.impl->sync.videoFrameWait = false;
|
||||||
} else {
|
} else {
|
||||||
shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget * m_turboSpeed;
|
shouldRedoSamples = m_threadContext.impl->sync.fpsTarget != m_fpsTarget * m_turboSpeed;
|
||||||
m_threadContext.sync.fpsTarget = m_fpsTarget * m_turboSpeed;
|
m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_turboSpeed;
|
||||||
m_threadContext.sync.audioWait = true;
|
m_threadContext.impl->sync.audioWait = true;
|
||||||
m_threadContext.sync.videoFrameWait = false;
|
m_threadContext.impl->sync.videoFrameWait = false;
|
||||||
}
|
}
|
||||||
if (m_audioProcessor && shouldRedoSamples) {
|
if (m_audioProcessor && shouldRedoSamples) {
|
||||||
redoSamples(m_audioProcessor->getBufferSamples());
|
redoSamples(m_audioProcessor->getBufferSamples());
|
||||||
|
@ -1017,24 +1024,30 @@ void GameController::enableTurbo() {
|
||||||
void GameController::setSync(bool enable) {
|
void GameController::setSync(bool enable) {
|
||||||
m_turbo = false;
|
m_turbo = false;
|
||||||
m_turboForced = false;
|
m_turboForced = false;
|
||||||
if (!enable) {
|
if (isLoaded()) {
|
||||||
m_threadContext.sync.audioWait = false;
|
if (!enable) {
|
||||||
m_threadContext.sync.videoFrameWait = false;
|
m_threadContext.impl->sync.audioWait = false;
|
||||||
} else {
|
m_threadContext.impl->sync.videoFrameWait = false;
|
||||||
m_threadContext.sync.audioWait = m_audioSync;
|
} else {
|
||||||
m_threadContext.sync.videoFrameWait = m_videoSync;
|
m_threadContext.impl->sync.audioWait = m_audioSync;
|
||||||
|
m_threadContext.impl->sync.videoFrameWait = m_videoSync;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_sync = enable;
|
m_sync = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::setAudioSync(bool enable) {
|
void GameController::setAudioSync(bool enable) {
|
||||||
m_audioSync = enable;
|
m_audioSync = enable;
|
||||||
m_threadContext.sync.audioWait = enable;
|
if (isLoaded()) {
|
||||||
|
m_threadContext.impl->sync.audioWait = enable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::setVideoSync(bool enable) {
|
void GameController::setVideoSync(bool enable) {
|
||||||
m_videoSync = enable;
|
m_videoSync = enable;
|
||||||
m_threadContext.sync.videoFrameWait = enable;
|
if (isLoaded()) {
|
||||||
|
m_threadContext.impl->sync.videoFrameWait = enable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::setAVStream(mAVStream* stream) {
|
void GameController::setAVStream(mAVStream* stream) {
|
||||||
|
|
|
@ -718,14 +718,10 @@ void Window::toggleFullScreen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::gameStarted(mCoreThread* context, const QString& fname) {
|
void Window::gameStarted(mCoreThread* context, const QString& fname) {
|
||||||
MutexLock(&context->stateMutex);
|
if (!mCoreThreadIsActive(context)) {
|
||||||
if (context->state < THREAD_EXITING) {
|
|
||||||
emit startDrawing(context);
|
|
||||||
} else {
|
|
||||||
MutexUnlock(&context->stateMutex);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MutexUnlock(&context->stateMutex);
|
emit startDrawing(context);
|
||||||
for (QAction* action : m_gameActions) {
|
for (QAction* action : m_gameActions) {
|
||||||
action->setDisabled(false);
|
action->setDisabled(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
struct VideoBackend* v = &renderer->gl.d;
|
struct VideoBackend* v = &renderer->gl.d;
|
||||||
|
|
||||||
while (context->state < THREAD_EXITING) {
|
while (mCoreThreadIsActive(context)) {
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
mSDLHandleEvent(context, &renderer->player, &event);
|
mSDLHandleEvent(context, &renderer->player, &event);
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
|
@ -66,10 +66,10 @@ void mSDLGLRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCoreSyncWaitFrameStart(&context->sync)) {
|
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||||
v->postFrame(v, renderer->outputBuffer);
|
v->postFrame(v, renderer->outputBuffer);
|
||||||
}
|
}
|
||||||
mCoreSyncWaitFrameEnd(&context->sync);
|
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||||
v->drawFrame(v);
|
v->drawFrame(v);
|
||||||
v->swap(v);
|
v->swap(v);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext)
|
||||||
|
|
||||||
if (threadContext) {
|
if (threadContext) {
|
||||||
context->core = threadContext->core;
|
context->core = threadContext->core;
|
||||||
context->sync = &threadContext->sync;
|
context->sync = &threadContext->impl->sync;
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_PauseAudioDevice(context->deviceId, 0);
|
SDL_PauseAudioDevice(context->deviceId, 0);
|
||||||
|
|
|
@ -419,7 +419,7 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer*
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->keysym.sym == SDLK_TAB) {
|
if (event->keysym.sym == SDLK_TAB) {
|
||||||
context->sync.audioWait = event->type != SDL_KEYDOWN;
|
context->impl->sync.audioWait = event->type != SDL_KEYDOWN;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event->keysym.sym == SDLK_BACKQUOTE) {
|
if (event->keysym.sym == SDLK_BACKQUOTE) {
|
||||||
|
|
Loading…
Reference in New Issue