Make audio disposal thread safe on all 3 backends (#2527)
* Make audio disposal thread safe on all 3 backends * Make OpenAL more consistent with the other backends * Remove Window.Cursor = null, and change dummy TValue to byte
This commit is contained in:
parent
06cd3abe6c
commit
a27986c311
7 changed files with 45 additions and 115 deletions
|
@ -3,7 +3,7 @@ using Ryujinx.Audio.Common;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||||
|
@ -12,11 +12,10 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
{
|
{
|
||||||
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
|
public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
|
||||||
{
|
{
|
||||||
private object _lock = new object();
|
private readonly ALDevice _device;
|
||||||
private ALDevice _device;
|
private readonly ALContext _context;
|
||||||
private ALContext _context;
|
private readonly ManualResetEvent _updateRequiredEvent;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
private readonly ConcurrentDictionary<OpenALHardwareDeviceSession, byte> _sessions;
|
||||||
private List<OpenALHardwareDeviceSession> _sessions;
|
|
||||||
private bool _stillRunning;
|
private bool _stillRunning;
|
||||||
private Thread _updaterThread;
|
private Thread _updaterThread;
|
||||||
|
|
||||||
|
@ -25,7 +24,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
_device = ALC.OpenDevice("");
|
_device = ALC.OpenDevice("");
|
||||||
_context = ALC.CreateContext(_device, new ALContextAttributes());
|
_context = ALC.CreateContext(_device, new ALContextAttributes());
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_sessions = new List<OpenALHardwareDeviceSession>();
|
_sessions = new ConcurrentDictionary<OpenALHardwareDeviceSession, byte>();
|
||||||
|
|
||||||
_stillRunning = true;
|
_stillRunning = true;
|
||||||
_updaterThread = new Thread(Update)
|
_updaterThread = new Thread(Update)
|
||||||
|
@ -72,22 +71,16 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
throw new ArgumentException($"{channelCount}");
|
throw new ArgumentException($"{channelCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||||
|
|
||||||
_sessions.Add(session);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unregister(OpenALHardwareDeviceSession session)
|
internal bool Unregister(OpenALHardwareDeviceSession session)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
return _sessions.TryRemove(session, out _);
|
||||||
{
|
|
||||||
_sessions.Remove(session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManualResetEvent GetUpdateRequiredEvent()
|
public ManualResetEvent GetUpdateRequiredEvent()
|
||||||
|
@ -103,16 +96,13 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
{
|
{
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
|
|
||||||
lock (_lock)
|
foreach (OpenALHardwareDeviceSession session in _sessions.Keys)
|
||||||
{
|
|
||||||
foreach (OpenALHardwareDeviceSession session in _sessions)
|
|
||||||
{
|
{
|
||||||
if (session.Update())
|
if (session.Update())
|
||||||
{
|
{
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (updateRequired)
|
if (updateRequired)
|
||||||
{
|
{
|
||||||
|
@ -135,26 +125,10 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
{
|
{
|
||||||
_stillRunning = false;
|
_stillRunning = false;
|
||||||
|
|
||||||
int sessionCount = 0;
|
foreach (OpenALHardwareDeviceSession session in _sessions.Keys)
|
||||||
|
|
||||||
// NOTE: This is done in a way to avoid possible situations when the OpenALHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister.
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_sessions.Count == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenALHardwareDeviceSession session = _sessions[_sessions.Count - 1];
|
|
||||||
|
|
||||||
session.Dispose();
|
session.Dispose();
|
||||||
|
|
||||||
sessionCount = _sessions.Count;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
while (sessionCount > 0);
|
|
||||||
|
|
||||||
ALC.DestroyContext(_context);
|
ALC.DestroyContext(_context);
|
||||||
ALC.CloseDevice(_device);
|
ALC.CloseDevice(_device);
|
||||||
|
|
|
@ -190,7 +190,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing && _driver.Unregister(this))
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
@ -198,8 +198,6 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
AL.DeleteSource(_sourceId);
|
AL.DeleteSource(_sourceId);
|
||||||
|
|
||||||
_driver.Unregister(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
using Ryujinx.Audio.Backends.Common;
|
using Ryujinx.Audio.Common;
|
||||||
using Ryujinx.Audio.Common;
|
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.SDL2.Common;
|
using Ryujinx.SDL2.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -15,15 +14,13 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
{
|
{
|
||||||
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
|
public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver
|
||||||
{
|
{
|
||||||
private object _lock = new object();
|
private readonly ManualResetEvent _updateRequiredEvent;
|
||||||
|
private readonly ConcurrentDictionary<SDL2HardwareDeviceSession, byte> _sessions;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
|
||||||
private List<SDL2HardwareDeviceSession> _sessions;
|
|
||||||
|
|
||||||
public SDL2HardwareDeviceDriver()
|
public SDL2HardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_sessions = new List<SDL2HardwareDeviceSession>();
|
_sessions = new ConcurrentDictionary<SDL2HardwareDeviceSession, byte>();
|
||||||
|
|
||||||
SDL2Driver.Instance.Initialize();
|
SDL2Driver.Instance.Initialize();
|
||||||
}
|
}
|
||||||
|
@ -64,22 +61,16 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||||
|
|
||||||
_sessions.Add(session);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unregister(SDL2HardwareDeviceSession session)
|
internal bool Unregister(SDL2HardwareDeviceSession session)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
return _sessions.TryRemove(session, out _);
|
||||||
{
|
|
||||||
_sessions.Remove(session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
|
private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount)
|
||||||
|
@ -149,10 +140,8 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
while (_sessions.Count > 0)
|
foreach (SDL2HardwareDeviceSession session in _sessions.Keys)
|
||||||
{
|
{
|
||||||
SDL2HardwareDeviceSession session = _sessions[_sessions.Count - 1];
|
|
||||||
|
|
||||||
session.Dispose();
|
session.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing && _driver.Unregister(this))
|
||||||
{
|
{
|
||||||
PrepareToClose();
|
PrepareToClose();
|
||||||
Stop();
|
Stop();
|
||||||
|
@ -210,8 +210,6 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
{
|
{
|
||||||
SDL_CloseAudioDevice(_outputStream);
|
SDL_CloseAudioDevice(_outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
_driver.Unregister(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using SoundIOSharp;
|
using SoundIOSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||||
|
@ -12,19 +12,17 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
{
|
{
|
||||||
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
||||||
{
|
{
|
||||||
private object _lock = new object();
|
private readonly SoundIO _audioContext;
|
||||||
|
private readonly SoundIODevice _audioDevice;
|
||||||
private SoundIO _audioContext;
|
private readonly ManualResetEvent _updateRequiredEvent;
|
||||||
private SoundIODevice _audioDevice;
|
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
|
||||||
private List<SoundIoHardwareDeviceSession> _sessions;
|
|
||||||
private int _disposeState;
|
private int _disposeState;
|
||||||
|
|
||||||
public SoundIoHardwareDeviceDriver()
|
public SoundIoHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_audioContext = new SoundIO();
|
_audioContext = new SoundIO();
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_sessions = new List<SoundIoHardwareDeviceSession>();
|
_sessions = new ConcurrentDictionary<SoundIoHardwareDeviceSession, byte>();
|
||||||
|
|
||||||
_audioContext.Connect();
|
_audioContext.Connect();
|
||||||
_audioContext.FlushEvents();
|
_audioContext.FlushEvents();
|
||||||
|
@ -142,22 +140,16 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
||||||
|
|
||||||
_sessions.Add(session);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unregister(SoundIoHardwareDeviceSession session)
|
internal bool Unregister(SoundIoHardwareDeviceSession session)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
return _sessions.TryRemove(session, out _);
|
||||||
{
|
|
||||||
_sessions.Remove(session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
|
public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
|
||||||
|
@ -219,26 +211,10 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
int sessionCount = 0;
|
foreach (SoundIoHardwareDeviceSession session in _sessions.Keys)
|
||||||
|
|
||||||
// NOTE: This is done in a way to avoid possible situations when the SoundIoHardwareDeviceSession is already being dispose in another thread but doesn't hold the lock and tries to Unregister.
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_sessions.Count == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundIoHardwareDeviceSession session = _sessions[_sessions.Count - 1];
|
|
||||||
|
|
||||||
session.Dispose();
|
session.Dispose();
|
||||||
|
|
||||||
sessionCount = _sessions.Count;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
while (sessionCount > 0);
|
|
||||||
|
|
||||||
_audioContext.Disconnect();
|
_audioContext.Disconnect();
|
||||||
_audioContext.Dispose();
|
_audioContext.Dispose();
|
||||||
|
|
|
@ -423,14 +423,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing && _driver.Unregister(this))
|
||||||
{
|
{
|
||||||
PrepareToClose();
|
PrepareToClose();
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
_outputStream.Dispose();
|
_outputStream.Dispose();
|
||||||
|
|
||||||
_driver.Unregister(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Configuration;
|
using Ryujinx.Configuration;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.GTK3;
|
using Ryujinx.Input.GTK3;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
|
@ -138,8 +137,6 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
|
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
|
||||||
|
|
||||||
Window.Cursor = null;
|
|
||||||
|
|
||||||
NpadManager.Dispose();
|
NpadManager.Dispose();
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
@ -151,7 +148,7 @@ namespace Ryujinx.Ui
|
||||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ConfigurationState.Instance.Hid.EnableMouse)
|
if (ConfigurationState.Instance.Hid.EnableMouse)
|
||||||
{
|
{
|
||||||
Window.Cursor = _invisibleCursor;
|
Window.Cursor = _invisibleCursor;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue