SplitterContext.cs 12 KB

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