SplitterContext.cs 12 KB

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