diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Audio/Renderer/Server/MemoryPool | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Audio/Renderer/Server/MemoryPool')
3 files changed, 629 insertions, 0 deletions
diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs new file mode 100644 index 00000000..5fd6b2b9 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs @@ -0,0 +1,133 @@ +using System; +using System.Runtime.InteropServices; +using CpuAddress = System.UInt64; +using DspAddress = System.UInt64; + +namespace Ryujinx.Audio.Renderer.Server.MemoryPool +{ + /// <summary> + /// Represents the information of a region shared between the CPU and DSP. + /// </summary> + public struct AddressInfo + { + /// <summary> + /// The target CPU address of the region. + /// </summary> + public CpuAddress CpuAddress; + + /// <summary> + /// The size of the region. + /// </summary> + public ulong Size; + + private unsafe MemoryPoolState* _memoryPools; + + /// <summary> + /// The forced DSP address of the region. + /// </summary> + public DspAddress ForceMappedDspAddress; + + private unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; + + public unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; + + /// <summary> + /// Create an new empty <see cref="AddressInfo"/>. + /// </summary> + /// <returns>A new empty <see cref="AddressInfo"/>.</returns> + public static AddressInfo Create() + { + return Create(0, 0); + } + + /// <summary> + /// Create a new <see cref="AddressInfo"/>. + /// </summary> + /// <param name="cpuAddress">The target <see cref="CpuAddress"/> of the region.</param> + /// <param name="size">The target size of the region.</param> + /// <returns>A new <see cref="AddressInfo"/>.</returns> + public static AddressInfo Create(CpuAddress cpuAddress, ulong size) + { + unsafe + { + return new AddressInfo + { + CpuAddress = cpuAddress, + _memoryPools = MemoryPoolState.Null, + Size = size, + ForceMappedDspAddress = 0 + }; + } + } + + /// <summary> + /// Setup the CPU address and size of the <see cref="AddressInfo"/>. + /// </summary> + /// <param name="cpuAddress">The target <see cref="CpuAddress"/> of the region.</param> + /// <param name="size">The size of the region.</param> + public void Setup(CpuAddress cpuAddress, ulong size) + { + CpuAddress = cpuAddress; + Size = size; + ForceMappedDspAddress = 0; + + unsafe + { + _memoryPools = MemoryPoolState.Null; + } + } + + /// <summary> + /// Set the <see cref="MemoryPoolState"/> associated. + /// </summary> + /// <param name="memoryPoolState">The <see cref="MemoryPoolState"/> associated.</param> + public void SetupMemoryPool(Span<MemoryPoolState> memoryPoolState) + { + unsafe + { + fixed (MemoryPoolState* ptr = &MemoryMarshal.GetReference(memoryPoolState)) + { + SetupMemoryPool(ptr); + } + } + } + + /// <summary> + /// Set the <see cref="MemoryPoolState"/> associated. + /// </summary> + /// <param name="memoryPoolState">The <see cref="MemoryPoolState"/> associated.</param> + public unsafe void SetupMemoryPool(MemoryPoolState* memoryPoolState) + { + _memoryPools = memoryPoolState; + } + + /// <summary> + /// Check if the <see cref="MemoryPoolState"/> is mapped. + /// </summary> + /// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped.</returns> + public bool HasMappedMemoryPool() + { + return HasMemoryPoolState && MemoryPoolState.IsMapped(); + } + + /// <summary> + /// Get the DSP address associated to the <see cref="AddressInfo"/>. + /// </summary> + /// <param name="markUsed">If true, mark the <see cref="MemoryPoolState"/> as used.</param> + /// <returns>Returns the DSP address associated to the <see cref="AddressInfo"/>.</returns> + public DspAddress GetReference(bool markUsed) + { + if (!HasMappedMemoryPool()) + { + return ForceMappedDspAddress; + } + + if (markUsed) + { + MemoryPoolState.IsUsed = true; + } + + return MemoryPoolState.Translate(CpuAddress, Size); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs new file mode 100644 index 00000000..69466bab --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.InteropServices; +using CpuAddress = System.UInt64; +using DspAddress = System.UInt64; + +namespace Ryujinx.Audio.Renderer.Server.MemoryPool +{ + /// <summary> + /// Server state for a memory pool. + /// </summary> + [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)] + public struct MemoryPoolState + { + public const int Alignment = 0x10; + + /// <summary> + /// The location of the <see cref="MemoryPoolState"/>. + /// </summary> + public enum LocationType : uint + { + /// <summary> + /// <see cref="MemoryPoolState"/> located on the CPU side for user use. + /// </summary> + Cpu, + + /// <summary> + /// <see cref="MemoryPoolState"/> located on the DSP side for system use. + /// </summary> + Dsp + } + + /// <summary> + /// The CPU address associated to the <see cref="MemoryPoolState"/>. + /// </summary> + public CpuAddress CpuAddress; + + /// <summary> + /// The DSP address associated to the <see cref="MemoryPoolState"/>. + /// </summary> + public DspAddress DspAddress; + + /// <summary> + /// The size associated to the <see cref="MemoryPoolState"/>. + /// </summary> + public ulong Size; + + /// <summary> + /// The <see cref="LocationType"/> associated to the <see cref="MemoryPoolState"/>. + /// </summary> + public LocationType Location; + + /// <summary> + /// Set to true if the <see cref="MemoryPoolState"/> is used. + /// </summary> + [MarshalAs(UnmanagedType.I1)] + public bool IsUsed; + + public static unsafe MemoryPoolState* Null => (MemoryPoolState*)IntPtr.Zero.ToPointer(); + + /// <summary> + /// Create a new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>. + /// </summary> + /// <param name="location">The location type to use.</param> + /// <returns>A new <see cref="MemoryPoolState"/> with the given <see cref="LocationType"/>.</returns> + public static MemoryPoolState Create(LocationType location) + { + return new MemoryPoolState + { + CpuAddress = 0, + DspAddress = 0, + Size = 0, + Location = location + }; + } + + /// <summary> + /// Set the <see cref="CpuAddress"/> and size of the <see cref="MemoryPoolState"/>. + /// </summary> + /// <param name="cpuAddress">The <see cref="CpuAddress"/>.</param> + /// <param name="size">The size.</param> + public void SetCpuAddress(CpuAddress cpuAddress, ulong size) + { + CpuAddress = cpuAddress; + Size = size; + } + + /// <summary> + /// Check if the given <see cref="CpuAddress"/> and size is contains in the <see cref="MemoryPoolState"/>. + /// </summary> + /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param> + /// <param name="size">The size.</param> + /// <returns>True if the <see cref="CpuAddress"/> is contained inside the <see cref="MemoryPoolState"/>.</returns> + public bool Contains(CpuAddress targetCpuAddress, ulong size) + { + if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress) + { + return true; + } + + return false; + } + + /// <summary> + /// Translate the given CPU address to a DSP address. + /// </summary> + /// <param name="targetCpuAddress">The <see cref="CpuAddress"/>.</param> + /// <param name="size">The size.</param> + /// <returns>the target DSP address.</returns> + public DspAddress Translate(CpuAddress targetCpuAddress, ulong size) + { + if (Contains(targetCpuAddress, size) && IsMapped()) + { + ulong offset = targetCpuAddress - CpuAddress; + + return DspAddress + offset; + } + + return 0; + } + + /// <summary> + /// Is the <see cref="MemoryPoolState"/> mapped on the DSP? + /// </summary> + /// <returns>Returns true if the <see cref="MemoryPoolState"/> is mapped on the DSP.</returns> + public bool IsMapped() + { + return DspAddress != 0; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs new file mode 100644 index 00000000..6c79da15 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs @@ -0,0 +1,366 @@ +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Utils; +using Ryujinx.Common.Logging; +using System; +using static Ryujinx.Audio.Renderer.Common.BehaviourParameter; + +using CpuAddress = System.UInt64; +using DspAddress = System.UInt64; + +namespace Ryujinx.Audio.Renderer.Server.MemoryPool +{ + /// <summary> + /// Memory pool mapping helper. + /// </summary> + public class PoolMapper + { + const uint CurrentProcessPseudoHandle = 0xFFFF8001; + + /// <summary> + /// The result of <see cref="Update(ref MemoryPoolState, ref MemoryPoolInParameter, ref MemoryPoolOutStatus)"/>. + /// </summary> + public enum UpdateResult : uint + { + /// <summary> + /// No error reported. + /// </summary> + Success = 0, + + /// <summary> + /// The user parameters were invalid. + /// </summary> + InvalidParameter = 1, + + /// <summary> + /// <see cref="Dsp.AudioProcessor"/> mapping failed. + /// </summary> + MapError = 2, + + /// <summary> + /// <see cref="Dsp.AudioProcessor"/> unmapping failed. + /// </summary> + UnmapError = 3 + } + + /// <summary> + /// The handle of the process owning the CPU memory manipulated. + /// </summary> + private uint _processHandle; + + /// <summary> + /// The <see cref="Memory{MemoryPoolState}"/> that will be manipulated. + /// </summary> + private Memory<MemoryPoolState> _memoryPools; + + /// <summary> + /// If set to true, this will try to force map memory pool even if their state are considered invalid. + /// </summary> + private bool _isForceMapEnabled; + + /// <summary> + /// Create a new <see cref="PoolMapper"/> used for system mapping. + /// </summary> + /// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param> + /// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param> + public PoolMapper(uint processHandle, bool isForceMapEnabled) + { + _processHandle = processHandle; + _isForceMapEnabled = isForceMapEnabled; + _memoryPools = Memory<MemoryPoolState>.Empty; + } + + /// <summary> + /// Create a new <see cref="PoolMapper"/> used for user mapping. + /// </summary> + /// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param> + /// <param name="memoryPool">The user memory pools.</param> + /// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param> + public PoolMapper(uint processHandle, Memory<MemoryPoolState> memoryPool, bool isForceMapEnabled) + { + _processHandle = processHandle; + _memoryPools = memoryPool; + _isForceMapEnabled = isForceMapEnabled; + } + + /// <summary> + /// Initialize the <see cref="MemoryPoolState"/> for system use. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/> for system use.</param> + /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param> + /// <param name="size">The size to assign.</param> + /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns> + public bool InitializeSystemPool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) + { + if (memoryPool.Location != MemoryPoolState.LocationType.Dsp) + { + return false; + } + + return InitializePool(ref memoryPool, cpuAddress, size); + } + + /// <summary> + /// Initialize the <see cref="MemoryPoolState"/>. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param> + /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param> + /// <param name="size">The size to assign.</param> + /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns> + public bool InitializePool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) + { + memoryPool.SetCpuAddress(cpuAddress, size); + + return Map(ref memoryPool) != 0; + } + + /// <summary> + /// Get the process handle associated to the <see cref="MemoryPoolState"/>. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param> + /// <returns>Returns the process handle associated to the <see cref="MemoryPoolState"/>.</returns> + public uint GetProcessHandle(ref MemoryPoolState memoryPool) + { + if (memoryPool.Location == MemoryPoolState.LocationType.Cpu) + { + return CurrentProcessPseudoHandle; + } + else if (memoryPool.Location == MemoryPoolState.LocationType.Dsp) + { + return _processHandle; + } + + return 0; + } + + /// <summary> + /// Map the <see cref="MemoryPoolState"/> on the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to map.</param> + /// <returns>Returns the DSP address mapped.</returns> + public DspAddress Map(ref MemoryPoolState memoryPool) + { + DspAddress result = AudioProcessorMemoryManager.Map(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size); + + if (result != 0) + { + memoryPool.DspAddress = result; + } + + return result; + } + + /// <summary> + /// Unmap the <see cref="MemoryPoolState"/> from the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to unmap.</param> + /// <returns>Returns true if unmapped.</returns> + public bool Unmap(ref MemoryPoolState memoryPool) + { + if (memoryPool.IsUsed) + { + return false; + } + + AudioProcessorMemoryManager.Unmap(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size); + + memoryPool.SetCpuAddress(0, 0); + memoryPool.DspAddress = 0; + + return true; + } + + /// <summary> + /// Find a <see cref="MemoryPoolState"/> associated to the region given. + /// </summary> + /// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param> + /// <param name="size">The region size.</param> + /// <returns>Returns the <see cref="MemoryPoolState"/> found or <see cref="Memory{MemoryPoolState}.Empty"/> if not found.</returns> + private Span<MemoryPoolState> FindMemoryPool(CpuAddress cpuAddress, ulong size) + { + if (!_memoryPools.IsEmpty && _memoryPools.Length > 0) + { + for (int i = 0; i < _memoryPools.Length; i++) + { + if (_memoryPools.Span[i].Contains(cpuAddress, size)) + { + return _memoryPools.Span.Slice(i, 1); + } + } + } + + return Span<MemoryPoolState>.Empty; + } + + /// <summary> + /// Force unmap the given <see cref="AddressInfo"/>. + /// </summary> + /// <param name="addressInfo">The <see cref="AddressInfo"/> to force unmap</param> + public void ForceUnmap(ref AddressInfo addressInfo) + { + if (_isForceMapEnabled) + { + Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); + + if (!memoryPool.IsEmpty) + { + AudioProcessorMemoryManager.Unmap(_processHandle, memoryPool[0].CpuAddress, memoryPool[0].Size); + + return; + } + + AudioProcessorMemoryManager.Unmap(_processHandle, addressInfo.CpuAddress, 0); + } + } + + /// <summary> + /// Try to attach the given region to the <see cref="AddressInfo"/>. + /// </summary> + /// <param name="errorInfo">The error information if an error was generated.</param> + /// <param name="addressInfo">The <see cref="AddressInfo"/> to attach the region to.</param> + /// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param> + /// <param name="size">The region size.</param> + /// <returns>Returns true if mapping was performed.</returns> + public bool TryAttachBuffer(out ErrorInfo errorInfo, ref AddressInfo addressInfo, CpuAddress cpuAddress, ulong size) + { + errorInfo = new ErrorInfo(); + + addressInfo.Setup(cpuAddress, size); + + if (AssignDspAddress(ref addressInfo)) + { + errorInfo.ErrorCode = 0x0; + errorInfo.ExtraErrorInfo = 0x0; + + return true; + } + else + { + errorInfo.ErrorCode = ResultCode.InvalidAddressInfo; + errorInfo.ExtraErrorInfo = addressInfo.CpuAddress; + + return _isForceMapEnabled; + } + } + + /// <summary> + /// Update a <see cref="MemoryPoolState"/> using user parameters. + /// </summary> + /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to update.</param> + /// <param name="inParameter">Input user parameter.</param> + /// <param name="outStatus">Output user parameter.</param> + /// <returns>Returns the <see cref="UpdateResult"/> of the operations performed.</returns> + public UpdateResult Update(ref MemoryPoolState memoryPool, ref MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus) + { + MemoryPoolUserState inputState = inParameter.State; + + MemoryPoolUserState outputState; + + const uint pageSize = 0x1000; + + if (inputState != MemoryPoolUserState.RequestAttach && inputState != MemoryPoolUserState.RequestDetach) + { + return UpdateResult.Success; + } + + if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % pageSize) != 0) + { + return UpdateResult.InvalidParameter; + } + + if (inParameter.Size == 0 || (inParameter.Size % pageSize) != 0) + { + return UpdateResult.InvalidParameter; + } + + if (inputState == MemoryPoolUserState.RequestAttach) + { + bool initializeSuccess = InitializePool(ref memoryPool, inParameter.CpuAddress, inParameter.Size); + + if (!initializeSuccess) + { + memoryPool.SetCpuAddress(0, 0); + + Logger.Error?.Print(LogClass.AudioRenderer, $"Map of memory pool (address: 0x{inParameter.CpuAddress:x}, size 0x{inParameter.Size:x}) failed!"); + return UpdateResult.MapError; + } + + outputState = MemoryPoolUserState.Attached; + } + else + { + if (memoryPool.CpuAddress != inParameter.CpuAddress || memoryPool.Size != inParameter.Size) + { + return UpdateResult.InvalidParameter; + } + + if (!Unmap(ref memoryPool)) + { + Logger.Error?.Print(LogClass.AudioRenderer, $"Unmap of memory pool (address: 0x{memoryPool.CpuAddress:x}, size 0x{memoryPool.Size:x}) failed!"); + return UpdateResult.UnmapError; + } + + outputState = MemoryPoolUserState.Detached; + } + + outStatus.State = outputState; + + return UpdateResult.Success; + } + + /// <summary> + /// Map the <see cref="AddressInfo"/> to the <see cref="Dsp.AudioProcessor"/>. + /// </summary> + /// <param name="addressInfo">The <see cref="AddressInfo"/> to map.</param> + /// <returns>Returns true if mapping was performed.</returns> + private bool AssignDspAddress(ref AddressInfo addressInfo) + { + if (addressInfo.CpuAddress == 0) + { + return false; + } + + if (_memoryPools.Length > 0) + { + Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size); + + if (!memoryPool.IsEmpty) + { + addressInfo.SetupMemoryPool(memoryPool); + + return true; + } + } + + if (_isForceMapEnabled) + { + DspAddress dspAddress = AudioProcessorMemoryManager.Map(_processHandle, addressInfo.CpuAddress, addressInfo.Size); + + addressInfo.ForceMappedDspAddress = dspAddress; + + AudioProcessorMemoryManager.Map(_processHandle, addressInfo.CpuAddress, addressInfo.Size); + } + else + { + unsafe + { + addressInfo.SetupMemoryPool(MemoryPoolState.Null); + } + } + + return false; + } + + /// <summary> + /// Remove the usage flag from all the <see cref="MemoryPoolState"/>. + /// </summary> + /// <param name="memoryPool">The <see cref="Memory{MemoryPoolState}"/> to reset.</param> + public static void ClearUsageState(Memory<MemoryPoolState> memoryPool) + { + foreach (ref MemoryPoolState info in memoryPool.Span) + { + info.IsUsed = false; + } + } + } +}
\ No newline at end of file |
