From f556c80d0230056335632b60c71f1567e177239e Mon Sep 17 00:00:00 2001 From: Mary Date: Fri, 26 Feb 2021 01:11:56 +0100 Subject: Haydn: Part 1 (#2007) * Haydn: Part 1 Based on my reverse of audio 11.0.0. As always, core implementation under LGPLv3 for the same reasons as for Amadeus. This place the bases of a more flexible audio system while making audout & audin accurate. This have the following improvements: - Complete reimplementation of audout and audin. - Audin currently only have a dummy backend. - Dramatically reduce CPU usage by up to 50% in common cases (SoundIO and OpenAL). - Audio Renderer now can output to 5.1 devices when supported. - Audio Renderer init its backend on demand instead of keeping two up all the time. - All backends implementation are now in their own project. - Ryujinx.Audio.Renderer was renamed Ryujinx.Audio and was refactored because of this. As a note, games having issues with OpenAL haven't improved and will not because of OpenAL design (stopping when buffers finish playing causing possible audio "pops" when buffers are very small). * Update for latest hexkyz's edits on Switchbrew * audren: Rollback channel configuration changes * Address gdkchan's comments * Fix typo in OpenAL backend driver * Address last comments * Fix a nit * Address gdkchan's comments --- .../Server/MemoryPool/AddressInfo.cs | 151 -------- .../Server/MemoryPool/MemoryPoolState.cs | 148 -------- .../Server/MemoryPool/PoolMapper.cs | 383 --------------------- 3 files changed, 682 deletions(-) delete mode 100644 Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs delete mode 100644 Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs delete mode 100644 Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs (limited to 'Ryujinx.Audio.Renderer/Server/MemoryPool') diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs b/Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs deleted file mode 100644 index 2b625d06..00000000 --- a/Ryujinx.Audio.Renderer/Server/MemoryPool/AddressInfo.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright (c) 2019-2021 Ryujinx -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . -// - -using System; -using System.Runtime.InteropServices; - -using DspAddress = System.UInt64; -using CpuAddress = System.UInt64; - -namespace Ryujinx.Audio.Renderer.Server.MemoryPool -{ - /// - /// Represents the information of a region shared between the CPU and DSP. - /// - public struct AddressInfo - { - /// - /// The target CPU address of the region. - /// - public CpuAddress CpuAddress; - - /// - /// The size of the region. - /// - public ulong Size; - - private unsafe MemoryPoolState* _memoryPools; - - /// - /// The forced DSP address of the region. - /// - public DspAddress ForceMappedDspAddress; - - private unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; - - public unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; - - /// - /// Create an new empty . - /// - /// A new empty . - public static AddressInfo Create() - { - return Create(0, 0); - } - - /// - /// Create a new . - /// - /// The target of the region. - /// The target size of the region. - /// A new . - public static AddressInfo Create(CpuAddress cpuAddress, ulong size) - { - unsafe - { - return new AddressInfo - { - CpuAddress = cpuAddress, - _memoryPools = MemoryPoolState.Null, - Size = size, - ForceMappedDspAddress = 0 - }; - } - } - - /// - /// Setup the CPU address and size of the . - /// - /// The target of the region. - /// The size of the region. - public void Setup(CpuAddress cpuAddress, ulong size) - { - CpuAddress = cpuAddress; - Size = size; - ForceMappedDspAddress = 0; - - unsafe - { - _memoryPools = MemoryPoolState.Null; - } - } - - /// - /// Set the associated. - /// - /// The associated. - public void SetupMemoryPool(Span memoryPoolState) - { - unsafe - { - fixed (MemoryPoolState* ptr = &MemoryMarshal.GetReference(memoryPoolState)) - { - SetupMemoryPool(ptr); - } - } - } - - /// - /// Set the associated. - /// - /// The associated. - public unsafe void SetupMemoryPool(MemoryPoolState* memoryPoolState) - { - _memoryPools = memoryPoolState; - } - - /// - /// Check if the is mapped. - /// - /// Returns true if the is mapped. - public bool HasMappedMemoryPool() - { - return HasMemoryPoolState && MemoryPoolState.IsMapped(); - } - - /// - /// Get the DSP address associated to the . - /// - /// If true, mark the as used. - /// Returns the DSP address associated to the . - public DspAddress GetReference(bool markUsed) - { - if (!HasMappedMemoryPool()) - { - return ForceMappedDspAddress; - } - - if (markUsed) - { - MemoryPoolState.IsUsed = true; - } - - return MemoryPoolState.Translate(CpuAddress, Size); - } - } -} diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs b/Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs deleted file mode 100644 index ebde1403..00000000 --- a/Ryujinx.Audio.Renderer/Server/MemoryPool/MemoryPoolState.cs +++ /dev/null @@ -1,148 +0,0 @@ -// -// Copyright (c) 2019-2021 Ryujinx -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . -// - -using System; -using System.Runtime.InteropServices; - -using DspAddress = System.UInt64; -using CpuAddress = System.UInt64; - -namespace Ryujinx.Audio.Renderer.Server.MemoryPool -{ - /// - /// Server state for a memory pool. - /// - [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = Alignment)] - public struct MemoryPoolState - { - public const int Alignment = 0x10; - - /// - /// The location of the . - /// - public enum LocationType : uint - { - /// - /// located on the CPU side for user use. - /// - Cpu, - - /// - /// located on the DSP side for system use. - /// - Dsp - } - - /// - /// The CPU address associated to the . - /// - public CpuAddress CpuAddress; - - /// - /// The DSP address associated to the . - /// - public DspAddress DspAddress; - - /// - /// The size associated to the . - /// - public ulong Size; - - /// - /// The associated to the . - /// - public LocationType Location; - - /// - /// Set to true if the is used. - /// - [MarshalAs(UnmanagedType.I1)] - public bool IsUsed; - - public static unsafe MemoryPoolState* Null => (MemoryPoolState*)IntPtr.Zero.ToPointer(); - - /// - /// Create a new with the given . - /// - /// The location type to use. - /// A new with the given . - public static MemoryPoolState Create(LocationType location) - { - return new MemoryPoolState - { - CpuAddress = 0, - DspAddress = 0, - Size = 0, - Location = location - }; - } - - /// - /// Set the and size of the . - /// - /// The . - /// The size. - public void SetCpuAddress(CpuAddress cpuAddress, ulong size) - { - CpuAddress = cpuAddress; - Size = size; - } - - /// - /// Check if the given and size is contains in the . - /// - /// The . - /// The size. - /// True if the is contained inside the . - public bool Contains(CpuAddress targetCpuAddress, ulong size) - { - if (CpuAddress <= targetCpuAddress && size + targetCpuAddress <= Size + CpuAddress) - { - return true; - } - - return false; - } - - /// - /// Translate the given CPU address to a DSP address. - /// - /// The . - /// The size. - /// the target DSP address. - public DspAddress Translate(CpuAddress targetCpuAddress, ulong size) - { - if (Contains(targetCpuAddress, size) && IsMapped()) - { - ulong offset = targetCpuAddress - CpuAddress; - - return DspAddress + offset; - } - - return 0; - } - - /// - /// Is the mapped on the DSP? - /// - /// Returns true if the is mapped on the DSP. - public bool IsMapped() - { - return DspAddress != 0; - } - } -} diff --git a/Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs b/Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs deleted file mode 100644 index b255960c..00000000 --- a/Ryujinx.Audio.Renderer/Server/MemoryPool/PoolMapper.cs +++ /dev/null @@ -1,383 +0,0 @@ -// -// Copyright (c) 2019-2021 Ryujinx -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . -// - -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 -{ - /// - /// Memory pool mapping helper. - /// - public class PoolMapper - { - const uint CurrentProcessPseudoHandle = 0xFFFF8001; - - /// - /// The result of . - /// - public enum UpdateResult : uint - { - /// - /// No error reported. - /// - Success = 0, - - /// - /// The user parameters were invalid. - /// - InvalidParameter = 1, - - /// - /// mapping failed. - /// - MapError = 2, - - /// - /// unmapping failed. - /// - UnmapError = 3 - } - - /// - /// The handle of the process owning the CPU memory manipulated. - /// - private uint _processHandle; - - /// - /// The that will be manipulated. - /// - private Memory _memoryPools; - - /// - /// If set to true, this will try to force map memory pool even if their state are considered invalid. - /// - private bool _isForceMapEnabled; - - /// - /// Create a new used for system mapping. - /// - /// The handle of the process owning the CPU memory manipulated. - /// If set to true, this will try to force map memory pool even if their state are considered invalid. - public PoolMapper(uint processHandle, bool isForceMapEnabled) - { - _processHandle = processHandle; - _isForceMapEnabled = isForceMapEnabled; - _memoryPools = Memory.Empty; - } - - /// - /// Create a new used for user mapping. - /// - /// The handle of the process owning the CPU memory manipulated. - /// The user memory pools. - /// If set to true, this will try to force map memory pool even if their state are considered invalid. - public PoolMapper(uint processHandle, Memory memoryPool, bool isForceMapEnabled) - { - _processHandle = processHandle; - _memoryPools = memoryPool; - _isForceMapEnabled = isForceMapEnabled; - } - - /// - /// Initialize the for system use. - /// - /// The for system use. - /// The to assign. - /// The size to assign. - /// Returns true if mapping on the succeeded. - public bool InitializeSystemPool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) - { - if (memoryPool.Location != MemoryPoolState.LocationType.Dsp) - { - return false; - } - - return InitializePool(ref memoryPool, cpuAddress, size); - } - - /// - /// Initialize the . - /// - /// The . - /// The to assign. - /// The size to assign. - /// Returns true if mapping on the succeeded. - public bool InitializePool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size) - { - memoryPool.SetCpuAddress(cpuAddress, size); - - return Map(ref memoryPool) != 0; - } - - /// - /// Get the process handle associated to the . - /// - /// The . - /// Returns the process handle associated to the . - 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; - } - - /// - /// Map the on the . - /// - /// The to map. - /// Returns the DSP address mapped. - 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; - } - - /// - /// Unmap the from the . - /// - /// The to unmap. - /// Returns true if unmapped. - 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; - } - - /// - /// Find a associated to the region given. - /// - /// The region . - /// The region size. - /// Returns the found or if not found. - private Span 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.Empty; - } - - /// - /// Force unmap the given . - /// - /// The to force unmap - public void ForceUnmap(ref AddressInfo addressInfo) - { - if (_isForceMapEnabled) - { - Span 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); - } - } - - /// - /// Try to attach the given region to the . - /// - /// The error information if an error was generated. - /// The to attach the region to. - /// The region . - /// The region size. - /// Returns true if mapping was performed. - 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; - } - } - - /// - /// Update a using user parameters. - /// - /// The to update. - /// Input user parameter. - /// Output user parameter. - /// Returns the of the operations performed. - 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 - 1)) != 0) - { - return UpdateResult.InvalidParameter; - } - - if (inParameter.Size == 0 || (inParameter.Size & (pageSize - 1)) != 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; - } - - /// - /// Map the to the . - /// - /// The to map. - /// Returns true if mapping was performed. - private bool AssignDspAddress(ref AddressInfo addressInfo) - { - if (addressInfo.CpuAddress == 0) - { - return false; - } - - if (_memoryPools.Length > 0) - { - Span 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; - } - - /// - /// Remove the usage flag from all the . - /// - /// The to reset. - public static void ClearUsageState(Memory memoryPool) - { - foreach (ref MemoryPoolState info in memoryPool.Span) - { - info.IsUsed = false; - } - } - } -} -- cgit v1.2.3