SplitterContext.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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;
  21. using System;
  22. using System.Diagnostics;
  23. using System.Runtime.CompilerServices;
  24. using System.Runtime.InteropServices;
  25. namespace Ryujinx.Audio.Renderer.Server.Splitter
  26. {
  27. /// <summary>
  28. /// Splitter context.
  29. /// </summary>
  30. public class SplitterContext
  31. {
  32. /// <summary>
  33. /// Storage for <see cref="SplitterState"/>.
  34. /// </summary>
  35. private Memory<SplitterState> _splitters;
  36. /// <summary>
  37. /// Storage for <see cref="SplitterDestination"/>.
  38. /// </summary>
  39. private Memory<SplitterDestination> _splitterDestinations;
  40. /// <summary>
  41. /// If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, ref SplitterInParameter, ReadOnlySpan{byte})"/>.
  42. /// </summary>
  43. public bool IsBugFixed { get; private set; }
  44. /// <summary>
  45. /// Initialize <see cref="SplitterContext"/>.
  46. /// </summary>
  47. /// <param name="behaviourContext">The behaviour context.</param>
  48. /// <param name="parameter">The audio renderer configuration.</param>
  49. /// <param name="workBufferAllocator">The <see cref="WorkBufferAllocator"/>.</param>
  50. /// <returns>Return true if the initialization was successful.</returns>
  51. public bool Initialize(ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter, WorkBufferAllocator workBufferAllocator)
  52. {
  53. if (!behaviourContext.IsSplitterSupported() || parameter.SplitterCount <= 0 || parameter.SplitterDestinationCount <= 0)
  54. {
  55. Setup(Memory<SplitterState>.Empty, Memory<SplitterDestination>.Empty, false);
  56. return true;
  57. }
  58. Memory<SplitterState> splitters = workBufferAllocator.Allocate<SplitterState>(parameter.SplitterCount, SplitterState.Alignment);
  59. if (splitters.IsEmpty)
  60. {
  61. return false;
  62. }
  63. int splitterId = 0;
  64. foreach (ref SplitterState splitter in splitters.Span)
  65. {
  66. splitter = new SplitterState(splitterId++);
  67. }
  68. Memory<SplitterDestination> splitterDestinations = workBufferAllocator.Allocate<SplitterDestination>(parameter.SplitterDestinationCount,
  69. SplitterDestination.Alignment);
  70. if (splitterDestinations.IsEmpty)
  71. {
  72. return false;
  73. }
  74. int splitterDestinationId = 0;
  75. foreach (ref SplitterDestination data in splitterDestinations.Span)
  76. {
  77. data = new SplitterDestination(splitterDestinationId++);
  78. }
  79. SplitterState.InitializeSplitters(splitters.Span);
  80. Setup(splitters, splitterDestinations, behaviourContext.IsSplitterBugFixed());
  81. return true;
  82. }
  83. /// <summary>
  84. /// Get the work buffer size while adding the size needed for splitter to operate.
  85. /// </summary>
  86. /// <param name="size">The current size.</param>
  87. /// <param name="behaviourContext">The behaviour context.</param>
  88. /// <param name="parameter">The renderer configuration.</param>
  89. /// <returns>Return the new size taking splitter into account.</returns>
  90. public static ulong GetWorkBufferSize(ulong size, ref BehaviourContext behaviourContext, ref AudioRendererConfiguration parameter)
  91. {
  92. if (behaviourContext.IsSplitterSupported())
  93. {
  94. size = WorkBufferAllocator.GetTargetSize<SplitterState>(size, parameter.SplitterCount, SplitterState.Alignment);
  95. size = WorkBufferAllocator.GetTargetSize<SplitterDestination>(size, parameter.SplitterDestinationCount, SplitterDestination.Alignment);
  96. if (behaviourContext.IsSplitterBugFixed())
  97. {
  98. size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.SplitterDestinationCount, 0x10);
  99. }
  100. return size;
  101. }
  102. else
  103. {
  104. return size;
  105. }
  106. }
  107. /// <summary>
  108. /// Setup the <see cref="SplitterContext"/> instance.
  109. /// </summary>
  110. /// <param name="splitters">The <see cref="SplitterState"/> storage.</param>
  111. /// <param name="splitterDestinations">The <see cref="SplitterDestination"/> storage.</param>
  112. /// <param name="isBugFixed">If set to true, trust the user destination count in <see cref="SplitterState.Update(SplitterContext, ref SplitterInParameter, ReadOnlySpan{byte})"/>.</param>
  113. private void Setup(Memory<SplitterState> splitters, Memory<SplitterDestination> splitterDestinations, bool isBugFixed)
  114. {
  115. _splitters = splitters;
  116. _splitterDestinations = splitterDestinations;
  117. IsBugFixed = isBugFixed;
  118. }
  119. /// <summary>
  120. /// Clear the new connection flag.
  121. /// </summary>
  122. private void ClearAllNewConnectionFlag()
  123. {
  124. foreach (ref SplitterState splitter in _splitters.Span)
  125. {
  126. splitter.ClearNewConnectionFlag();
  127. }
  128. }
  129. /// <summary>
  130. /// Get the destination count using the count of splitter.
  131. /// </summary>
  132. /// <returns>The destination count using the count of splitter.</returns>
  133. public int GetDestinationCountPerStateForCompatibility()
  134. {
  135. if (_splitters.IsEmpty)
  136. {
  137. return 0;
  138. }
  139. return _splitterDestinations.Length / _splitters.Length;
  140. }
  141. /// <summary>
  142. /// Update one or multiple <see cref="SplitterState"/> from user parameters.
  143. /// </summary>
  144. /// <param name="inputHeader">The splitter header.</param>
  145. /// <param name="input">The raw data after the splitter header.</param>
  146. private void UpdateState(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
  147. {
  148. for (int i = 0; i < inputHeader.SplitterCount; i++)
  149. {
  150. SplitterInParameter parameter = MemoryMarshal.Read<SplitterInParameter>(input);
  151. Debug.Assert(parameter.IsMagicValid());
  152. if (parameter.IsMagicValid())
  153. {
  154. if (parameter.Id >= 0 && parameter.Id < _splitters.Length)
  155. {
  156. ref SplitterState splitter = ref GetState(parameter.Id);
  157. splitter.Update(this, ref parameter, input.Slice(Unsafe.SizeOf<SplitterInParameter>()));
  158. }
  159. input = input.Slice(0x1C + (int)parameter.DestinationCount * 4);
  160. }
  161. }
  162. }
  163. /// <summary>
  164. /// Update one or multiple <see cref="SplitterDestination"/> from user parameters.
  165. /// </summary>
  166. /// <param name="inputHeader">The splitter header.</param>
  167. /// <param name="input">The raw data after the splitter header.</param>
  168. private void UpdateData(ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan<byte> input)
  169. {
  170. for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
  171. {
  172. SplitterDestinationInParameter parameter = MemoryMarshal.Read<SplitterDestinationInParameter>(input);
  173. Debug.Assert(parameter.IsMagicValid());
  174. if (parameter.IsMagicValid())
  175. {
  176. if (parameter.Id >= 0 && parameter.Id < _splitterDestinations.Length)
  177. {
  178. ref SplitterDestination destination = ref GetDestination(parameter.Id);
  179. destination.Update(parameter);
  180. }
  181. input = input.Slice(Unsafe.SizeOf<SplitterDestinationInParameter>());
  182. }
  183. }
  184. }
  185. /// <summary>
  186. /// Update splitter from user parameters.
  187. /// </summary>
  188. /// <param name="input">The input raw user data.</param>
  189. /// <param name="consumedSize">The total consumed size.</param>
  190. /// <returns>Return true if the update was successful.</returns>
  191. public bool Update(ReadOnlySpan<byte> input, out int consumedSize)
  192. {
  193. if (_splitterDestinations.IsEmpty || _splitters.IsEmpty)
  194. {
  195. consumedSize = 0;
  196. return true;
  197. }
  198. int originalSize = input.Length;
  199. SplitterInParameterHeader header = SpanIOHelper.Read<SplitterInParameterHeader>(ref input);
  200. if (header.IsMagicValid())
  201. {
  202. ClearAllNewConnectionFlag();
  203. UpdateState(ref header, ref input);
  204. UpdateData(ref header, ref input);
  205. consumedSize = BitUtils.AlignUp(originalSize - input.Length, 0x10);
  206. return true;
  207. }
  208. else
  209. {
  210. consumedSize = 0;
  211. return false;
  212. }
  213. }
  214. /// <summary>
  215. /// Get a reference to a <see cref="SplitterState"/> at the given <paramref name="id"/>.
  216. /// </summary>
  217. /// <param name="id">The index to use.</param>
  218. /// <returns>A reference to a <see cref="SplitterState"/> at the given <paramref name="id"/>.</returns>
  219. public ref SplitterState GetState(int id)
  220. {
  221. return ref SpanIOHelper.GetFromMemory(_splitters, id, (uint)_splitters.Length);
  222. }
  223. /// <summary>
  224. /// Get a reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.
  225. /// </summary>
  226. /// <param name="id">The index to use.</param>
  227. /// <returns>A reference to a <see cref="SplitterDestination"/> at the given <paramref name="id"/>.</returns>
  228. public ref SplitterDestination GetDestination(int id)
  229. {
  230. return ref SpanIOHelper.GetFromMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
  231. }
  232. /// <summary>
  233. /// Get a <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>.
  234. /// </summary>
  235. /// <param name="id">The index to use.</param>
  236. /// <returns>A <see cref="Memory{SplitterDestination}"/> at the given <paramref name="id"/>.</returns>
  237. public Memory<SplitterDestination> GetDestinationMemory(int id)
  238. {
  239. return SpanIOHelper.GetMemory(_splitterDestinations, id, (uint)_splitterDestinations.Length);
  240. }
  241. /// <summary>
  242. /// Get a <see cref="Span{SplitterDestination}"/> in the <see cref="SplitterState"/> at <paramref name="id"/> and pass <paramref name="destinationId"/> to <see cref="SplitterState.GetData(int)"/>.
  243. /// </summary>
  244. /// <param name="id">The index to use to get the <see cref="SplitterState"/>.</param>
  245. /// <param name="destinationId">The index of the <see cref="SplitterDestination"/>.</param>
  246. /// <returns>A <see cref="Span{SplitterDestination}"/>.</returns>
  247. public Span<SplitterDestination> GetDestination(int id, int destinationId)
  248. {
  249. ref SplitterState splitter = ref GetState(id);
  250. return splitter.GetData(destinationId);
  251. }
  252. /// <summary>
  253. /// Return true if the audio renderer has any splitters.
  254. /// </summary>
  255. /// <returns>True if the audio renderer has any splitters.</returns>
  256. public bool UsingSplitter()
  257. {
  258. return !_splitters.IsEmpty && !_splitterDestinations.IsEmpty;
  259. }
  260. /// <summary>
  261. /// Update the internal state of all splitters.
  262. /// </summary>
  263. public void UpdateInternalState()
  264. {
  265. foreach (ref SplitterState splitter in _splitters.Span)
  266. {
  267. splitter.UpdateInternalState();
  268. }
  269. }
  270. }
  271. }