mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
made savestates synchronous and immediate. this allows saving or loading while the emulator is paused, fixes issues where savestate hotkeys would get ignored if pressed too close together, might speed up savestates in some cases, and hopefully makes savestates more stable too.
the intent is to replace the haphazard scheduling and finger-crossing associated with saving/loading with the correct and minimal necessary wait for each thread to reach a known safe location before commencing the savestate operation, and for any already-paused components to not need to be resumed to do so.
This commit is contained in:
@@ -118,4 +118,23 @@ namespace AudioCommon
|
||||
bool UseJIT() {
|
||||
return ac_Config.m_EnableJIT;
|
||||
}
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (soundStream)
|
||||
{
|
||||
// audio typically doesn't maintain its own "paused" state
|
||||
// (that's already handled by the CPU and whatever else being paused)
|
||||
// so it should be good enough to only lock/unlock here.
|
||||
CMixer* pMixer = soundStream->GetMixer();
|
||||
if (pMixer)
|
||||
{
|
||||
std::mutex& csMixing = pMixer->MixerCritical();
|
||||
if (doLock)
|
||||
csMixing.lock();
|
||||
else
|
||||
csMixing.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace AudioCommon
|
||||
void ShutdownSoundStream();
|
||||
std::vector<std::string> GetSoundBackends();
|
||||
bool UseJIT();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
}
|
||||
|
||||
#endif // _AUDIO_COMMON_H_
|
||||
|
||||
@@ -37,7 +37,9 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples)
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
if (PowerPC::GetState() != 0)
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
memset(samples, 0, numSamples * 4);
|
||||
@@ -164,7 +166,7 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||
while (num_samples + Common::AtomicLoad(m_numSamples) > MAX_SAMPLES)
|
||||
{
|
||||
if (*PowerPC::GetStatePtr() != 0)
|
||||
if (*PowerPC::GetStatePtr() != PowerPC::CPU_RUNNING || soundStream->IsMuted())
|
||||
break;
|
||||
// Shortcut key for Throttle Skipping
|
||||
if (Host_GetKeyState('\t'))
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define _MIXER_H_
|
||||
|
||||
#include "WaveFile.h"
|
||||
#include "StdMutex.h"
|
||||
|
||||
// 16 bit Stereo
|
||||
#define MAX_SAMPLES (1024 * 8)
|
||||
@@ -89,6 +90,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex& MixerCritical() { return m_csMixing; }
|
||||
|
||||
protected:
|
||||
unsigned int m_sampleRate;
|
||||
@@ -110,7 +112,7 @@ protected:
|
||||
u32 m_indexR;
|
||||
|
||||
bool m_AIplaying;
|
||||
|
||||
std::mutex m_csMixing;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
virtual void Stop() {}
|
||||
virtual void Update() {}
|
||||
virtual void Clear(bool mute) { m_muted = mute; }
|
||||
bool IsMuted() { return m_muted; }
|
||||
virtual void StartLogAudio(const char *filename) {
|
||||
if (! m_logAudio) {
|
||||
m_logAudio = true;
|
||||
|
||||
@@ -93,8 +93,6 @@ public:
|
||||
|
||||
virtual bool Initialize(void *&) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
virtual void RunLoop(bool enable) = 0;
|
||||
|
||||
virtual std::string GetName() = 0;
|
||||
@@ -131,6 +129,14 @@ public:
|
||||
static void PopulateList();
|
||||
static void ClearList();
|
||||
static void ActivateBackend(const std::string& name);
|
||||
|
||||
// waits until is paused and fully idle, and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally unpauses.
|
||||
// calls must be balanced and non-recursive (once with doLock true, then once with doLock false).
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) = 0;
|
||||
|
||||
// the implementation needs not do synchronization logic, because calls to it are surrounded by PauseAndLock now
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
};
|
||||
|
||||
extern std::vector<VideoBackend*> g_available_video_backends;
|
||||
@@ -139,7 +145,6 @@ extern VideoBackend* g_video_backend;
|
||||
// inherited by dx9/dx11/ogl backends
|
||||
class VideoBackendHardware : public VideoBackend
|
||||
{
|
||||
void DoState(PointerWrap &p);
|
||||
void RunLoop(bool enable);
|
||||
bool Initialize(void *&) { InitializeShared(); return true; }
|
||||
|
||||
@@ -169,6 +174,9 @@ class VideoBackendHardware : public VideoBackend
|
||||
writeFn16 Video_PEWrite16();
|
||||
writeFn32 Video_PEWrite32();
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
protected:
|
||||
void InitializeShared();
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "HW/GPFifo.h"
|
||||
#include "HW/AudioInterface.h"
|
||||
#include "HW/VideoInterface.h"
|
||||
#include "HW/EXI.h"
|
||||
#include "HW/SystemTimers.h"
|
||||
|
||||
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
|
||||
@@ -58,6 +59,7 @@
|
||||
#include "DSPEmulator.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "AudioCommon.h"
|
||||
#include "OnScreenDisplay.h"
|
||||
#ifdef _WIN32
|
||||
#include "EmuWindow.h"
|
||||
@@ -95,13 +97,15 @@ void Stop();
|
||||
|
||||
bool g_bStopping = false;
|
||||
bool g_bHwInit = false;
|
||||
bool g_bStarted = false;
|
||||
bool g_bRealWiimote = false;
|
||||
void *g_pWindowHandle = NULL;
|
||||
std::string g_stateFileName;
|
||||
std::thread g_EmuThread;
|
||||
|
||||
static std::thread g_cpu_thread;
|
||||
static bool g_requestRefreshInfo;
|
||||
static bool g_requestRefreshInfo = false;
|
||||
static int g_pauseAndLockDepth = 0;
|
||||
|
||||
SCoreStartupParameter g_CoreStartupParameter;
|
||||
|
||||
@@ -160,16 +164,35 @@ bool IsRunning()
|
||||
return (GetState() != CORE_UNINITIALIZED) || g_bHwInit;
|
||||
}
|
||||
|
||||
bool IsRunningAndStarted()
|
||||
{
|
||||
return g_bStarted;
|
||||
}
|
||||
|
||||
bool IsRunningInCurrentThread()
|
||||
{
|
||||
return IsRunning() && ((!g_cpu_thread.joinable()) || g_cpu_thread.get_id() == std::this_thread::get_id());
|
||||
return IsRunning() && IsCPUThread();
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
{
|
||||
return ((!g_cpu_thread.joinable()) || g_cpu_thread.get_id() == std::this_thread::get_id());
|
||||
return (g_cpu_thread.joinable() ? (g_cpu_thread.get_id() == std::this_thread::get_id()) : !g_bStarted);
|
||||
}
|
||||
|
||||
bool IsGPUThread()
|
||||
{
|
||||
const SCoreStartupParameter& _CoreParameter =
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
if (_CoreParameter.bCPUThread)
|
||||
{
|
||||
return (g_EmuThread.joinable() && (g_EmuThread.get_id() == std::this_thread::get_id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsCPUThread();
|
||||
}
|
||||
}
|
||||
|
||||
// This is called from the GUI thread. See the booting call schedule in
|
||||
// BootManager.cpp
|
||||
bool Init()
|
||||
@@ -300,9 +323,13 @@ void CpuThread()
|
||||
if (!g_stateFileName.empty())
|
||||
State::LoadAs(g_stateFileName);
|
||||
|
||||
g_bStarted = true;
|
||||
|
||||
// Enter CPU run loop. When we leave it - we are done.
|
||||
CCPU::Run();
|
||||
|
||||
g_bStarted = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,6 +350,8 @@ void FifoPlayerThread()
|
||||
if (_CoreParameter.bLockThreads)
|
||||
Common::SetCurrentThreadAffinity(1); // Force to first core
|
||||
|
||||
g_bStarted = true;
|
||||
|
||||
// Enter CPU run loop. When we leave it - we are done.
|
||||
if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename))
|
||||
{
|
||||
@@ -330,6 +359,8 @@ void FifoPlayerThread()
|
||||
FifoPlayer::GetInstance().Close();
|
||||
}
|
||||
|
||||
g_bStarted = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -554,6 +585,26 @@ void RequestRefreshInfo()
|
||||
g_requestRefreshInfo = true;
|
||||
}
|
||||
|
||||
bool PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
// let's support recursive locking to simplify things on the caller's side,
|
||||
// and let's do it at this outer level in case the individual systems don't support it.
|
||||
if (doLock ? g_pauseAndLockDepth++ : --g_pauseAndLockDepth)
|
||||
return true;
|
||||
|
||||
// first pause or unpause the cpu
|
||||
bool wasUnpaused = CCPU::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
ExpansionInterface::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
|
||||
// audio has to come after cpu, because cpu thread can wait for audio thread (m_throttle).
|
||||
AudioCommon::PauseAndLock(doLock, unpauseOnUnlock);
|
||||
DSP::GetDSPEmulator()->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
|
||||
// video has to come after cpu, because cpu thread can wait for video thread (s_efbAccessRequested).
|
||||
g_video_backend->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
return wasUnpaused;
|
||||
}
|
||||
|
||||
// Apply Frame Limit and Display FPS info
|
||||
// This should only be called from VI
|
||||
void VideoThrottle()
|
||||
@@ -583,6 +634,9 @@ void VideoThrottle()
|
||||
g_requestRefreshInfo = false;
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
if (ElapseTime == 0)
|
||||
ElapseTime = 1;
|
||||
|
||||
u32 FPS = Common::AtomicLoad(DrawnFrame) * 1000 / ElapseTime;
|
||||
u32 VPS = DrawnVideo * 1000 / ElapseTime;
|
||||
u32 Speed = DrawnVideo * (100 * 1000) / (VideoInterface::TargetRefreshRate * ElapseTime);
|
||||
|
||||
@@ -54,9 +54,11 @@ void Stop();
|
||||
std::string StopMessage(bool, std::string);
|
||||
|
||||
bool IsRunning();
|
||||
bool IsRunningAndStarted(); // is running and the cpu loop has been entered
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the cpu thread.
|
||||
bool IsCPUThread(); // this tells us whether we are the cpu thread.
|
||||
|
||||
bool IsGPUThread();
|
||||
|
||||
void SetState(EState _State);
|
||||
EState GetState();
|
||||
|
||||
@@ -87,6 +89,12 @@ bool ShouldSkipFrame(int skipped);
|
||||
void VideoThrottle();
|
||||
void RequestRefreshInfo();
|
||||
|
||||
// waits until all systems are paused and fully idle, and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally unpauses.
|
||||
// calls must be balanced (once with doLock true, then once with doLock false) but may be recursive.
|
||||
// the return value of the first call should be passed in as the second argument of the second call.
|
||||
bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
#ifdef RERECORDING
|
||||
|
||||
void FrameUpdate();
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void DoState(PointerWrap &p) = 0;
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) = 0;
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short) = 0;
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short) = 0;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace
|
||||
{
|
||||
static Common::Event m_StepEvent;
|
||||
static Common::Event *m_SyncEvent;
|
||||
static std::mutex m_csCpuOccupied;
|
||||
}
|
||||
|
||||
void CCPU::Init(int cpu_core)
|
||||
@@ -47,6 +48,7 @@ void CCPU::Shutdown()
|
||||
|
||||
void CCPU::Run()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_csCpuOccupied);
|
||||
Host_UpdateDisasmDialog();
|
||||
|
||||
while (true)
|
||||
@@ -60,8 +62,12 @@ reswitch:
|
||||
break;
|
||||
|
||||
case PowerPC::CPU_STEPPING:
|
||||
m_StepEvent.Wait();
|
||||
m_csCpuOccupied.unlock();
|
||||
|
||||
//1: wait for step command..
|
||||
m_StepEvent.Wait();
|
||||
|
||||
m_csCpuOccupied.lock();
|
||||
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN)
|
||||
return;
|
||||
if (PowerPC::GetState() != PowerPC::CPU_STEPPING)
|
||||
@@ -132,3 +138,26 @@ void CCPU::Break()
|
||||
{
|
||||
EnableStepping(true);
|
||||
}
|
||||
|
||||
bool CCPU::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
bool wasUnpaused = !IsStepping();
|
||||
if (doLock)
|
||||
{
|
||||
// we can't use EnableStepping, that would causes deadlocks with both audio and video
|
||||
PowerPC::Pause();
|
||||
if (!Core::IsCPUThread())
|
||||
m_csCpuOccupied.lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unpauseOnUnlock)
|
||||
{
|
||||
PowerPC::Start();
|
||||
m_StepEvent.Set();
|
||||
}
|
||||
if (!Core::IsCPUThread())
|
||||
m_csCpuOccupied.unlock();
|
||||
}
|
||||
return wasUnpaused;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ public:
|
||||
|
||||
// is stepping ?
|
||||
static bool IsStepping();
|
||||
|
||||
// waits until is stepping and is ready for a command (paused and fully idle), and acquires a lock on that state.
|
||||
// or, if doLock is false, releases a lock on that state and optionally re-disables stepping.
|
||||
// calls must be balanced and non-recursive (once with doLock true, then once with doLock false).
|
||||
// intended (but not required) to be called from another thread,
|
||||
// e.g. when the GUI thread wants to make sure everything is paused so that it can create a savestate.
|
||||
// the return value is whether the cpu was unpaused before the call.
|
||||
static bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -130,7 +130,24 @@ void DSPHLE::SwapUCode(u32 _crc)
|
||||
|
||||
void DSPHLE::DoState(PointerWrap &p)
|
||||
{
|
||||
bool prevInitMixer = m_InitMixer;
|
||||
p.Do(m_InitMixer);
|
||||
if (prevInitMixer != m_InitMixer && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
if (m_InitMixer)
|
||||
{
|
||||
InitMixer();
|
||||
AudioCommon::PauseAndLock(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioCommon::PauseAndLock(false);
|
||||
soundStream->Stop();
|
||||
delete soundStream;
|
||||
soundStream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
p.Do(m_DSPControl);
|
||||
p.Do(m_dspState);
|
||||
|
||||
@@ -230,6 +247,17 @@ void DSPHLE::DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short _Value)
|
||||
}
|
||||
}
|
||||
|
||||
void DSPHLE::InitMixer()
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
soundStream = AudioCommon::InitSoundStream(new HLEMixer(this, AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
}
|
||||
|
||||
// Other DSP fuctions
|
||||
u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
@@ -238,14 +266,7 @@ u16 DSPHLE::DSP_WriteControlRegister(unsigned short _Value)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
|
||||
soundStream = AudioCommon::InitSoundStream(
|
||||
new HLEMixer(this, AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
InitMixer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +316,9 @@ void DSPHLE::DSP_ClearAudioBuffer(bool mute)
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
void DSPHLE::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (doLock || unpauseOnUnlock)
|
||||
DSP_ClearAudioBuffer(doLock);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
virtual bool IsLLE() { return false; }
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
@@ -55,6 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
void SendMailToDSP(u32 _uMail);
|
||||
void InitMixer();
|
||||
|
||||
// Declarations and definitions
|
||||
void *m_hWnd;
|
||||
|
||||
@@ -79,6 +79,24 @@ void DSPLLE::DoState(PointerWrap &p)
|
||||
p.DoArray(g_dsp.dram, DSP_DRAM_SIZE);
|
||||
p.Do(cyclesLeft);
|
||||
p.Do(m_cycle_count);
|
||||
|
||||
bool prevInitMixer = m_InitMixer;
|
||||
p.Do(m_InitMixer);
|
||||
if (prevInitMixer != m_InitMixer && p.GetMode() == PointerWrap::MODE_READ)
|
||||
{
|
||||
if (m_InitMixer)
|
||||
{
|
||||
InitMixer();
|
||||
AudioCommon::PauseAndLock(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioCommon::PauseAndLock(false);
|
||||
soundStream->Stop();
|
||||
delete soundStream;
|
||||
soundStream = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular thread
|
||||
@@ -110,7 +128,9 @@ void DSPLLE::dsp_thread(DSPLLE *dsp_lle)
|
||||
while (dsp_lle->m_bIsRunning)
|
||||
{
|
||||
int cycles = (int)dsp_lle->m_cycle_count;
|
||||
if (cycles > 0) {
|
||||
if (cycles > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(dsp_lle->m_csDSPThreadActive);
|
||||
if (dspjit)
|
||||
{
|
||||
DSPCore_RunCycles(cycles);
|
||||
@@ -179,6 +199,17 @@ void DSPLLE::Shutdown()
|
||||
DSPCore_Shutdown();
|
||||
}
|
||||
|
||||
void DSPLLE::InitMixer()
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
}
|
||||
|
||||
u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
||||
{
|
||||
UDSPControl Temp(_uFlag);
|
||||
@@ -186,12 +217,7 @@ u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
||||
{
|
||||
if (!Temp.DSPHalt)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DACSampleRate, ac_Config.iFrequency), m_hWnd);
|
||||
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||
// Mixer is initialized
|
||||
m_InitMixer = true;
|
||||
InitMixer();
|
||||
}
|
||||
}
|
||||
DSPInterpreter::WriteCR(_uFlag);
|
||||
@@ -334,3 +360,15 @@ void DSPLLE::DSP_ClearAudioBuffer(bool mute)
|
||||
if (soundStream)
|
||||
soundStream->Clear(mute);
|
||||
}
|
||||
|
||||
void DSPLLE::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
if (doLock || unpauseOnUnlock)
|
||||
DSP_ClearAudioBuffer(doLock);
|
||||
|
||||
if (doLock)
|
||||
m_csDSPThreadActive.lock();
|
||||
else
|
||||
m_csDSPThreadActive.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual bool IsLLE() { return true; }
|
||||
|
||||
virtual void DoState(PointerWrap &p);
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
|
||||
virtual void DSP_WriteMailBoxHigh(bool _CPUMailbox, unsigned short);
|
||||
virtual void DSP_WriteMailBoxLow(bool _CPUMailbox, unsigned short);
|
||||
@@ -46,8 +47,10 @@ public:
|
||||
|
||||
private:
|
||||
static void dsp_thread(DSPLLE* lpParameter);
|
||||
void InitMixer();
|
||||
|
||||
std::thread m_hDSPThread;
|
||||
std::mutex m_csDSPThreadActive;
|
||||
bool m_InitMixer;
|
||||
void *m_hWnd;
|
||||
bool m_bWii;
|
||||
|
||||
@@ -74,6 +74,13 @@ void OnAfterLoad()
|
||||
g_Channels[c]->OnAfterLoad();
|
||||
}
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
for (int c = 0; c < NUM_CHANNELS; ++c)
|
||||
g_Channels[c]->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
}
|
||||
|
||||
|
||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
u8 channel = (u8)(userdata >> 32);
|
||||
@@ -91,6 +98,17 @@ void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 devi
|
||||
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | ((u64)device_type << 16) | device_num);
|
||||
}
|
||||
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex)
|
||||
{
|
||||
for (int i = 0; i < NUM_CHANNELS; ++i)
|
||||
{
|
||||
IEXIDevice* device = g_Channels[i]->FindDevice(device_type, customIndex);
|
||||
if (device)
|
||||
return device;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Unused (?!)
|
||||
void Update()
|
||||
{
|
||||
|
||||
@@ -29,12 +29,14 @@ void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
|
||||
|
||||
void Update();
|
||||
void UpdateInterrupts();
|
||||
|
||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate);
|
||||
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
|
||||
@@ -315,3 +315,19 @@ void CEXIChannel::OnAfterLoad()
|
||||
m_pDevices[d]->OnAfterLoad();
|
||||
}
|
||||
|
||||
void CEXIChannel::PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
||||
{
|
||||
for (int d = 0; d < NUM_DEVICES; ++d)
|
||||
m_pDevices[d]->PauseAndLock(doLock, unpauseOnUnlock);
|
||||
}
|
||||
|
||||
IEXIDevice* CEXIChannel::FindDevice(TEXIDevices device_type, int customIndex)
|
||||
{
|
||||
for (int d = 0; d < NUM_DEVICES; ++d)
|
||||
{
|
||||
IEXIDevice* device = m_pDevices[d]->FindDevice(device_type, customIndex);
|
||||
if (device)
|
||||
return device;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ private:
|
||||
public:
|
||||
// get device
|
||||
IEXIDevice* GetDevice(const u8 _CHIP_SELECT);
|
||||
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
|
||||
|
||||
CEXIChannel(u32 ChannelId);
|
||||
~CEXIChannel();
|
||||
@@ -131,6 +132,7 @@ public:
|
||||
void UpdateInterrupts();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
|
||||
|
||||
// This should only be used to transition interrupts from SP1 to Channel 2
|
||||
void SetEXIINT(bool exiint) { m_Status.EXIINT = !!exiint; }
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
virtual void SetCS(int) {}
|
||||
virtual void DoState(PointerWrap&) {}
|
||||
virtual void OnAfterLoad() {}
|
||||
virtual void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) {}
|
||||
virtual IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1) { return (device_type == m_deviceType) ? this : NULL; }
|
||||
|
||||
// Update
|
||||
virtual void Update() {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user