PoolMapper.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. //
  2. // Copyright (c) 2019-2020 Ryujinx
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. //
  17. using Ryujinx.Audio.Renderer.Common;
  18. using Ryujinx.Audio.Renderer.Parameter;
  19. using Ryujinx.Audio.Renderer.Utils;
  20. using Ryujinx.Common.Logging;
  21. using System;
  22. using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
  23. using CpuAddress = System.UInt64;
  24. using DspAddress = System.UInt64;
  25. namespace Ryujinx.Audio.Renderer.Server.MemoryPool
  26. {
  27. /// <summary>
  28. /// Memory pool mapping helper.
  29. /// </summary>
  30. public class PoolMapper
  31. {
  32. const uint CurrentProcessPseudoHandle = 0xFFFF8001;
  33. /// <summary>
  34. /// The result of <see cref="Update(ref MemoryPoolState, ref MemoryPoolInParameter, ref MemoryPoolOutStatus)"/>.
  35. /// </summary>
  36. public enum UpdateResult : uint
  37. {
  38. /// <summary>
  39. /// No error reported.
  40. /// </summary>
  41. Success = 0,
  42. /// <summary>
  43. /// The user parameters were invalid.
  44. /// </summary>
  45. InvalidParameter = 1,
  46. /// <summary>
  47. /// <see cref="Dsp.AudioProcessor"/> mapping failed.
  48. /// </summary>
  49. MapError = 2,
  50. /// <summary>
  51. /// <see cref="Dsp.AudioProcessor"/> unmapping failed.
  52. /// </summary>
  53. UnmapError = 3
  54. }
  55. /// <summary>
  56. /// The handle of the process owning the CPU memory manipulated.
  57. /// </summary>
  58. private uint _processHandle;
  59. /// <summary>
  60. /// The <see cref="Memory{MemoryPoolState}"/> that will be manipulated.
  61. /// </summary>
  62. private Memory<MemoryPoolState> _memoryPools;
  63. /// <summary>
  64. /// If set to true, this will try to force map memory pool even if their state are considered invalid.
  65. /// </summary>
  66. private bool _isForceMapEnabled;
  67. /// <summary>
  68. /// Create a new <see cref="PoolMapper"/> used for system mapping.
  69. /// </summary>
  70. /// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param>
  71. /// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param>
  72. public PoolMapper(uint processHandle, bool isForceMapEnabled)
  73. {
  74. _processHandle = processHandle;
  75. _isForceMapEnabled = isForceMapEnabled;
  76. _memoryPools = Memory<MemoryPoolState>.Empty;
  77. }
  78. /// <summary>
  79. /// Create a new <see cref="PoolMapper"/> used for user mapping.
  80. /// </summary>
  81. /// <param name="processHandle">The handle of the process owning the CPU memory manipulated.</param>
  82. /// <param name="memoryPool">The user memory pools.</param>
  83. /// <param name="isForceMapEnabled">If set to true, this will try to force map memory pool even if their state are considered invalid.</param>
  84. public PoolMapper(uint processHandle, Memory<MemoryPoolState> memoryPool, bool isForceMapEnabled)
  85. {
  86. _processHandle = processHandle;
  87. _memoryPools = memoryPool;
  88. _isForceMapEnabled = isForceMapEnabled;
  89. }
  90. /// <summary>
  91. /// Initialize the <see cref="MemoryPoolState"/> for system use.
  92. /// </summary>
  93. /// <param name="memoryPool">The <see cref="MemoryPoolState"/> for system use.</param>
  94. /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param>
  95. /// <param name="size">The size to assign.</param>
  96. /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns>
  97. public bool InitializeSystemPool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size)
  98. {
  99. if (memoryPool.Location != MemoryPoolState.LocationType.Dsp)
  100. {
  101. return false;
  102. }
  103. return InitializePool(ref memoryPool, cpuAddress, size);
  104. }
  105. /// <summary>
  106. /// Initialize the <see cref="MemoryPoolState"/>.
  107. /// </summary>
  108. /// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param>
  109. /// <param name="cpuAddress">The <see cref="CpuAddress"/> to assign.</param>
  110. /// <param name="size">The size to assign.</param>
  111. /// <returns>Returns true if mapping on the <see cref="Dsp.AudioProcessor"/> succeeded.</returns>
  112. public bool InitializePool(ref MemoryPoolState memoryPool, CpuAddress cpuAddress, ulong size)
  113. {
  114. memoryPool.SetCpuAddress(cpuAddress, size);
  115. return Map(ref memoryPool) != 0;
  116. }
  117. /// <summary>
  118. /// Get the process handle associated to the <see cref="MemoryPoolState"/>.
  119. /// </summary>
  120. /// <param name="memoryPool">The <see cref="MemoryPoolState"/>.</param>
  121. /// <returns>Returns the process handle associated to the <see cref="MemoryPoolState"/>.</returns>
  122. public uint GetProcessHandle(ref MemoryPoolState memoryPool)
  123. {
  124. if (memoryPool.Location == MemoryPoolState.LocationType.Cpu)
  125. {
  126. return CurrentProcessPseudoHandle;
  127. }
  128. else if (memoryPool.Location == MemoryPoolState.LocationType.Dsp)
  129. {
  130. return _processHandle;
  131. }
  132. return 0;
  133. }
  134. /// <summary>
  135. /// Map the <see cref="MemoryPoolState"/> on the <see cref="Dsp.AudioProcessor"/>.
  136. /// </summary>
  137. /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to map.</param>
  138. /// <returns>Returns the DSP address mapped.</returns>
  139. public DspAddress Map(ref MemoryPoolState memoryPool)
  140. {
  141. DspAddress result = AudioProcessorMemoryManager.Map(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size);
  142. if (result != 0)
  143. {
  144. memoryPool.DspAddress = result;
  145. }
  146. return result;
  147. }
  148. /// <summary>
  149. /// Unmap the <see cref="MemoryPoolState"/> from the <see cref="Dsp.AudioProcessor"/>.
  150. /// </summary>
  151. /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to unmap.</param>
  152. /// <returns>Returns true if unmapped.</returns>
  153. public bool Unmap(ref MemoryPoolState memoryPool)
  154. {
  155. if (memoryPool.IsUsed)
  156. {
  157. return false;
  158. }
  159. AudioProcessorMemoryManager.Unmap(GetProcessHandle(ref memoryPool), memoryPool.CpuAddress, memoryPool.Size);
  160. memoryPool.SetCpuAddress(0, 0);
  161. memoryPool.DspAddress = 0;
  162. return true;
  163. }
  164. /// <summary>
  165. /// Find a <see cref="MemoryPoolState"/> associated to the region given.
  166. /// </summary>
  167. /// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param>
  168. /// <param name="size">The region size.</param>
  169. /// <returns>Returns the <see cref="MemoryPoolState"/> found or <see cref="Memory{MemoryPoolState}.Empty"/> if not found.</returns>
  170. private Span<MemoryPoolState> FindMemoryPool(CpuAddress cpuAddress, ulong size)
  171. {
  172. if (!_memoryPools.IsEmpty && _memoryPools.Length > 0)
  173. {
  174. for (int i = 0; i < _memoryPools.Length; i++)
  175. {
  176. if (_memoryPools.Span[i].Contains(cpuAddress, size))
  177. {
  178. return _memoryPools.Span.Slice(i, 1);
  179. }
  180. }
  181. }
  182. return Span<MemoryPoolState>.Empty;
  183. }
  184. /// <summary>
  185. /// Force unmap the given <see cref="AddressInfo"/>.
  186. /// </summary>
  187. /// <param name="addressInfo">The <see cref="AddressInfo"/> to force unmap</param>
  188. public void ForceUnmap(ref AddressInfo addressInfo)
  189. {
  190. if (_isForceMapEnabled)
  191. {
  192. Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size);
  193. if (!memoryPool.IsEmpty)
  194. {
  195. AudioProcessorMemoryManager.Unmap(_processHandle, memoryPool[0].CpuAddress, memoryPool[0].Size);
  196. return;
  197. }
  198. AudioProcessorMemoryManager.Unmap(_processHandle, addressInfo.CpuAddress, 0);
  199. }
  200. }
  201. /// <summary>
  202. /// Try to attach the given region to the <see cref="AddressInfo"/>.
  203. /// </summary>
  204. /// <param name="errorInfo">The error information if an error was generated.</param>
  205. /// <param name="addressInfo">The <see cref="AddressInfo"/> to attach the region to.</param>
  206. /// <param name="cpuAddress">The region <see cref="CpuAddress"/>.</param>
  207. /// <param name="size">The region size.</param>
  208. /// <returns>Returns true if mapping was performed.</returns>
  209. public bool TryAttachBuffer(out ErrorInfo errorInfo, ref AddressInfo addressInfo, CpuAddress cpuAddress, ulong size)
  210. {
  211. errorInfo = new ErrorInfo();
  212. addressInfo.Setup(cpuAddress, size);
  213. if (AssignDspAddress(ref addressInfo))
  214. {
  215. errorInfo.ErrorCode = 0x0;
  216. errorInfo.ExtraErrorInfo = 0x0;
  217. return true;
  218. }
  219. else
  220. {
  221. errorInfo.ErrorCode = ResultCode.InvalidAddressInfo;
  222. errorInfo.ExtraErrorInfo = addressInfo.CpuAddress;
  223. return _isForceMapEnabled;
  224. }
  225. }
  226. /// <summary>
  227. /// Update a <see cref="MemoryPoolState"/> using user parameters.
  228. /// </summary>
  229. /// <param name="memoryPool">The <see cref="MemoryPoolState"/> to update.</param>
  230. /// <param name="inParameter">Input user parameter.</param>
  231. /// <param name="outStatus">Output user parameter.</param>
  232. /// <returns>Returns the <see cref="UpdateResult"/> of the operations performed.</returns>
  233. public UpdateResult Update(ref MemoryPoolState memoryPool, ref MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
  234. {
  235. MemoryPoolUserState inputState = inParameter.State;
  236. MemoryPoolUserState outputState;
  237. const uint pageSize = 0x1000;
  238. if (inputState != MemoryPoolUserState.RequestAttach && inputState != MemoryPoolUserState.RequestDetach)
  239. {
  240. return UpdateResult.Success;
  241. }
  242. if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress & (pageSize - 1)) != 0)
  243. {
  244. return UpdateResult.InvalidParameter;
  245. }
  246. if (inParameter.Size == 0 || (inParameter.Size & (pageSize - 1)) != 0)
  247. {
  248. return UpdateResult.InvalidParameter;
  249. }
  250. if (inputState == MemoryPoolUserState.RequestAttach)
  251. {
  252. bool initializeSuccess = InitializePool(ref memoryPool, inParameter.CpuAddress, inParameter.Size);
  253. if (!initializeSuccess)
  254. {
  255. memoryPool.SetCpuAddress(0, 0);
  256. Logger.Error?.Print(LogClass.AudioRenderer, $"Map of memory pool (address: 0x{inParameter.CpuAddress:x}, size 0x{inParameter.Size:x}) failed!");
  257. return UpdateResult.MapError;
  258. }
  259. outputState = MemoryPoolUserState.Attached;
  260. }
  261. else
  262. {
  263. if (memoryPool.CpuAddress != inParameter.CpuAddress || memoryPool.Size != inParameter.Size)
  264. {
  265. return UpdateResult.InvalidParameter;
  266. }
  267. if (!Unmap(ref memoryPool))
  268. {
  269. Logger.Error?.Print(LogClass.AudioRenderer, $"Unmap of memory pool (address: 0x{memoryPool.CpuAddress:x}, size 0x{memoryPool.Size:x}) failed!");
  270. return UpdateResult.UnmapError;
  271. }
  272. outputState = MemoryPoolUserState.Detached;
  273. }
  274. outStatus.State = outputState;
  275. return UpdateResult.Success;
  276. }
  277. /// <summary>
  278. /// Map the <see cref="AddressInfo"/> to the <see cref="Dsp.AudioProcessor"/>.
  279. /// </summary>
  280. /// <param name="addressInfo">The <see cref="AddressInfo"/> to map.</param>
  281. /// <returns>Returns true if mapping was performed.</returns>
  282. private bool AssignDspAddress(ref AddressInfo addressInfo)
  283. {
  284. if (addressInfo.CpuAddress == 0)
  285. {
  286. return false;
  287. }
  288. if (_memoryPools.Length > 0)
  289. {
  290. Span<MemoryPoolState> memoryPool = FindMemoryPool(addressInfo.CpuAddress, addressInfo.Size);
  291. if (!memoryPool.IsEmpty)
  292. {
  293. addressInfo.SetupMemoryPool(memoryPool);
  294. return true;
  295. }
  296. }
  297. if (_isForceMapEnabled)
  298. {
  299. DspAddress dspAddress = AudioProcessorMemoryManager.Map(_processHandle, addressInfo.CpuAddress, addressInfo.Size);
  300. addressInfo.ForceMappedDspAddress = dspAddress;
  301. AudioProcessorMemoryManager.Map(_processHandle, addressInfo.CpuAddress, addressInfo.Size);
  302. }
  303. else
  304. {
  305. unsafe
  306. {
  307. addressInfo.SetupMemoryPool(MemoryPoolState.Null);
  308. }
  309. }
  310. return false;
  311. }
  312. /// <summary>
  313. /// Remove the usage flag from all the <see cref="MemoryPoolState"/>.
  314. /// </summary>
  315. /// <param name="memoryPool">The <see cref="Memory{MemoryPoolState}"/> to reset.</param>
  316. public static void ClearUsageState(Memory<MemoryPoolState> memoryPool)
  317. {
  318. foreach (ref MemoryPoolState info in memoryPool.Span)
  319. {
  320. info.IsUsed = false;
  321. }
  322. }
  323. }
  324. }