Implement a "Pause Emulation" option & hotkey (#2428)

* Add a "Pause Emulation" option and hotkey

Closes Ryujinx#1604

* Refactoring how pause is handled

* Applied suggested changes from review

* Applied suggested fixes

* Pass correct suspend type to threads for suspend/resume

* Fix NRE after stoping emulation

* Removing SimulateWakeUpMessage call after resuming emulation

* Skip suspending non game process

* Pause the tickCounter in the ExecutionContext

* Refactoring tickCounter pause/resume as suggested

* Fix Config migration to add pause hotkey

* Fixed pausing only application threads

* Fix exiting emulator while paused

* Avoid pause/resume while already paused/resumed

* Cleanup unused code

* Avoid restarting audio if stopping emulation while in pause.

* Added suggested changes

* Fix ConfigurationState
This commit is contained in:
mpnico 2021-09-11 22:08:25 +02:00 committed by GitHub
parent b0e410a828
commit 117e32a6ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 311 additions and 54 deletions

View file

@ -45,6 +45,8 @@ namespace Ryujinx.Audio
/// </summary>
private Thread _workerThread;
private bool _isRunning;
/// <summary>
/// Create a new <see cref="AudioManager"/>.
/// </summary>
@ -52,6 +54,7 @@ namespace Ryujinx.Audio
{
_updateRequiredEvents = new ManualResetEvent[2];
_actions = new Action[2];
_isRunning = false;
// Termination event.
_updateRequiredEvents[1] = new ManualResetEvent(false);
@ -72,6 +75,7 @@ namespace Ryujinx.Audio
throw new InvalidOperationException();
}
_isRunning = true;
_workerThread.Start();
}
@ -96,7 +100,7 @@ namespace Ryujinx.Audio
/// </summary>
private void Update()
{
while (true)
while (_isRunning)
{
int index = WaitHandle.WaitAny(_updateRequiredEvents);
@ -118,6 +122,14 @@ namespace Ryujinx.Audio
}
}
/// <summary>
/// Stop updating the <see cref="AudioManager"/> without stopping the worker thread.
/// </summary>
public void StopUpdates()
{
_isRunning = false;
}
public void Dispose()
{
Dispose(true);

View file

@ -47,6 +47,11 @@ namespace Ryujinx.Audio.Backends.CompatLayer
return _realDriver.GetUpdateRequiredEvent();
}
public ManualResetEvent GetPauseEvent()
{
return _realDriver.GetPauseEvent();
}
private uint SelectHardwareChannelCount(uint targetChannelCount)
{
if (_realDriver.SupportsChannelCount(targetChannelCount))

View file

@ -27,10 +27,12 @@ namespace Ryujinx.Audio.Backends.Dummy
public class DummyHardwareDeviceDriver : IHardwareDeviceDriver
{
private ManualResetEvent _updateRequiredEvent;
private ManualResetEvent _pauseEvent;
public DummyHardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
@ -60,6 +62,11 @@ namespace Ryujinx.Audio.Backends.Dummy
return _updateRequiredEvent;
}
public ManualResetEvent GetPauseEvent()
{
return _pauseEvent;
}
public void Dispose()
{
Dispose(true);
@ -70,6 +77,7 @@ namespace Ryujinx.Audio.Backends.Dummy
if (disposing)
{
// NOTE: The _updateRequiredEvent will be disposed somewhere else.
_pauseEvent.Dispose();
}
}

View file

@ -36,6 +36,7 @@ namespace Ryujinx.Audio.Integration
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount);
ManualResetEvent GetUpdateRequiredEvent();
ManualResetEvent GetPauseEvent();
bool SupportsDirection(Direction direction);
bool SupportsSampleRate(uint sampleRate);

View file

@ -55,6 +55,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
private long _playbackEnds;
private ManualResetEvent _event;
private ManualResetEvent _pauseEvent;
public AudioProcessor()
{
_event = new ManualResetEvent(false);
@ -94,6 +96,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
_sessionCommandList = new RendererSession[Constants.AudioRendererSessionCountMax];
_event.Reset();
_lastTime = PerformanceCounter.ElapsedNanoseconds;
_pauseEvent = deviceDriver.GetPauseEvent();
StartThread();
@ -202,6 +205,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
while (true)
{
_pauseEvent?.WaitOne();
MailboxMessage message = _mailbox.ReceiveMessage();
if (message == MailboxMessage.Stop)

View file

@ -214,6 +214,14 @@ namespace Ryujinx.Audio.Renderer.Server
Logger.Info?.Print(LogClass.AudioRenderer, "Stopped audio renderer");
}
/// <summary>
/// Stop sending commands to the <see cref="AudioProcessor"/> without stopping the worker thread.
/// </summary>
public void StopSendingCommands()
{
_isRunning = false;
}
/// <summary>
/// Worker main function. This is used to dispatch audio renderer commands to the <see cref="AudioProcessor"/>.
/// </summary>