AudioRenderSystem.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. using Ryujinx.Audio.Integration;
  2. using Ryujinx.Audio.Renderer.Common;
  3. using Ryujinx.Audio.Renderer.Dsp.Command;
  4. using Ryujinx.Audio.Renderer.Parameter;
  5. using Ryujinx.Audio.Renderer.Server.Effect;
  6. using Ryujinx.Audio.Renderer.Server.MemoryPool;
  7. using Ryujinx.Audio.Renderer.Server.Mix;
  8. using Ryujinx.Audio.Renderer.Server.Performance;
  9. using Ryujinx.Audio.Renderer.Server.Sink;
  10. using Ryujinx.Audio.Renderer.Server.Splitter;
  11. using Ryujinx.Audio.Renderer.Server.Types;
  12. using Ryujinx.Audio.Renderer.Server.Upsampler;
  13. using Ryujinx.Audio.Renderer.Server.Voice;
  14. using Ryujinx.Audio.Renderer.Utils;
  15. using Ryujinx.Common;
  16. using Ryujinx.Common.Logging;
  17. using Ryujinx.Memory;
  18. using System;
  19. using System.Buffers;
  20. using System.Diagnostics;
  21. using System.Threading;
  22. using CpuAddress = System.UInt64;
  23. namespace Ryujinx.Audio.Renderer.Server
  24. {
  25. public class AudioRenderSystem : IDisposable
  26. {
  27. private object _lock = new object();
  28. private AudioRendererExecutionMode _executionMode;
  29. private IWritableEvent _systemEvent;
  30. private ManualResetEvent _terminationEvent;
  31. private MemoryPoolState _dspMemoryPoolState;
  32. private VoiceContext _voiceContext;
  33. private MixContext _mixContext;
  34. private SinkContext _sinkContext;
  35. private SplitterContext _splitterContext;
  36. private EffectContext _effectContext;
  37. private PerformanceManager _performanceManager;
  38. private UpsamplerManager _upsamplerManager;
  39. private bool _isActive;
  40. private BehaviourContext _behaviourContext;
  41. private ulong _totalElapsedTicksUpdating;
  42. private ulong _totalElapsedTicks;
  43. private int _sessionId;
  44. private Memory<MemoryPoolState> _memoryPools;
  45. private uint _sampleRate;
  46. private uint _sampleCount;
  47. private uint _mixBufferCount;
  48. private uint _voiceChannelCountMax;
  49. private uint _upsamplerCount;
  50. private uint _memoryPoolCount;
  51. private uint _processHandle;
  52. private ulong _appletResourceId;
  53. private WritableRegion _workBufferRegion;
  54. private MemoryHandle _workBufferMemoryPin;
  55. private Memory<float> _mixBuffer;
  56. private Memory<float> _depopBuffer;
  57. private uint _renderingTimeLimitPercent;
  58. private bool _voiceDropEnabled;
  59. private uint _voiceDropCount;
  60. private bool _isDspRunningBehind;
  61. private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator;
  62. private Memory<byte> _performanceBuffer;
  63. public IVirtualMemoryManager MemoryManager { get; private set; }
  64. private ulong _elapsedFrameCount;
  65. private ulong _renderingStartTick;
  66. private AudioRendererManager _manager;
  67. private int _disposeState;
  68. public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent)
  69. {
  70. _manager = manager;
  71. _terminationEvent = new ManualResetEvent(false);
  72. _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp);
  73. _voiceContext = new VoiceContext();
  74. _mixContext = new MixContext();
  75. _sinkContext = new SinkContext();
  76. _splitterContext = new SplitterContext();
  77. _effectContext = new EffectContext();
  78. _commandProcessingTimeEstimator = null;
  79. _systemEvent = systemEvent;
  80. _behaviourContext = new BehaviourContext();
  81. _totalElapsedTicksUpdating = 0;
  82. _sessionId = 0;
  83. }
  84. public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, IVirtualMemoryManager memoryManager)
  85. {
  86. if (!BehaviourContext.CheckValidRevision(parameter.Revision))
  87. {
  88. return ResultCode.OperationFailed;
  89. }
  90. if (GetWorkBufferSize(ref parameter) > workBufferSize)
  91. {
  92. return ResultCode.WorkBufferTooSmall;
  93. }
  94. Debug.Assert(parameter.RenderingDevice == AudioRendererRenderingDevice.Dsp && parameter.ExecutionMode == AudioRendererExecutionMode.Auto);
  95. Logger.Info?.Print(LogClass.AudioRenderer, $"Initializing with REV{BehaviourContext.GetRevisionNumber(parameter.Revision)}");
  96. _behaviourContext.SetUserRevision(parameter.Revision);
  97. _sampleRate = parameter.SampleRate;
  98. _sampleCount = parameter.SampleCount;
  99. _mixBufferCount = parameter.MixBufferCount;
  100. _voiceChannelCountMax = Constants.VoiceChannelCountMax;
  101. _upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
  102. _appletResourceId = appletResourceId;
  103. _memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
  104. _executionMode = parameter.ExecutionMode;
  105. _sessionId = sessionId;
  106. MemoryManager = memoryManager;
  107. if (memoryManager is IRefCounted rc)
  108. {
  109. rc.IncrementReferenceCount();
  110. }
  111. WorkBufferAllocator workBufferAllocator;
  112. _workBufferRegion = MemoryManager.GetWritableRegion(workBuffer, (int)workBufferSize);
  113. _workBufferRegion.Memory.Span.Fill(0);
  114. _workBufferMemoryPin = _workBufferRegion.Memory.Pin();
  115. workBufferAllocator = new WorkBufferAllocator(_workBufferRegion.Memory);
  116. PoolMapper poolMapper = new PoolMapper(processHandle, false);
  117. poolMapper.InitializeSystemPool(ref _dspMemoryPoolState, workBuffer, workBufferSize);
  118. _mixBuffer = workBufferAllocator.Allocate<float>(_sampleCount * (_voiceChannelCountMax + _mixBufferCount), 0x10);
  119. if (_mixBuffer.IsEmpty)
  120. {
  121. return ResultCode.WorkBufferTooSmall;
  122. }
  123. Memory<float> upSamplerWorkBuffer = workBufferAllocator.Allocate<float>(Constants.TargetSampleCount * (_voiceChannelCountMax + _mixBufferCount) * _upsamplerCount, 0x10);
  124. if (upSamplerWorkBuffer.IsEmpty)
  125. {
  126. return ResultCode.WorkBufferTooSmall;
  127. }
  128. _depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
  129. if (_depopBuffer.IsEmpty)
  130. {
  131. return ResultCode.WorkBufferTooSmall;
  132. }
  133. // Invalidate DSP cache on what was currently allocated with workBuffer.
  134. AudioProcessorMemoryManager.InvalidateDspCache(_dspMemoryPoolState.Translate(workBuffer, workBufferAllocator.Offset), workBufferAllocator.Offset);
  135. Debug.Assert((workBufferAllocator.Offset % Constants.BufferAlignment) == 0);
  136. Memory<VoiceState> voices = workBufferAllocator.Allocate<VoiceState>(parameter.VoiceCount, VoiceState.Alignment);
  137. if (voices.IsEmpty)
  138. {
  139. return ResultCode.WorkBufferTooSmall;
  140. }
  141. foreach (ref VoiceState voice in voices.Span)
  142. {
  143. voice.Initialize();
  144. }
  145. // A pain to handle as we can't have VoiceState*, use indices to be a bit more safe
  146. Memory<int> sortedVoices = workBufferAllocator.Allocate<int>(parameter.VoiceCount, 0x10);
  147. if (sortedVoices.IsEmpty)
  148. {
  149. return ResultCode.WorkBufferTooSmall;
  150. }
  151. // Clear memory (use -1 as it's an invalid index)
  152. sortedVoices.Span.Fill(-1);
  153. Memory<VoiceChannelResource> voiceChannelResources = workBufferAllocator.Allocate<VoiceChannelResource>(parameter.VoiceCount, VoiceChannelResource.Alignment);
  154. if (voiceChannelResources.IsEmpty)
  155. {
  156. return ResultCode.WorkBufferTooSmall;
  157. }
  158. for (uint id = 0; id < voiceChannelResources.Length; id++)
  159. {
  160. ref VoiceChannelResource voiceChannelResource = ref voiceChannelResources.Span[(int)id];
  161. voiceChannelResource.Id = id;
  162. voiceChannelResource.IsUsed = false;
  163. }
  164. Memory<VoiceUpdateState> voiceUpdateStates = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align);
  165. if (voiceUpdateStates.IsEmpty)
  166. {
  167. return ResultCode.WorkBufferTooSmall;
  168. }
  169. uint mixesCount = parameter.SubMixBufferCount + 1;
  170. Memory<MixState> mixes = workBufferAllocator.Allocate<MixState>(mixesCount, MixState.Alignment);
  171. if (mixes.IsEmpty)
  172. {
  173. return ResultCode.WorkBufferTooSmall;
  174. }
  175. if (parameter.EffectCount == 0)
  176. {
  177. foreach (ref MixState mix in mixes.Span)
  178. {
  179. mix = new MixState(Memory<int>.Empty, ref _behaviourContext);
  180. }
  181. }
  182. else
  183. {
  184. Memory<int> effectProcessingOrderArray = workBufferAllocator.Allocate<int>(parameter.EffectCount * mixesCount, 0x10);
  185. foreach (ref MixState mix in mixes.Span)
  186. {
  187. mix = new MixState(effectProcessingOrderArray.Slice(0, (int)parameter.EffectCount), ref _behaviourContext);
  188. effectProcessingOrderArray = effectProcessingOrderArray.Slice((int)parameter.EffectCount);
  189. }
  190. }
  191. // Initialize the final mix id
  192. mixes.Span[0].MixId = Constants.FinalMixId;
  193. Memory<int> sortedMixesState = workBufferAllocator.Allocate<int>(mixesCount, 0x10);
  194. if (sortedMixesState.IsEmpty)
  195. {
  196. return ResultCode.WorkBufferTooSmall;
  197. }
  198. // Clear memory (use -1 as it's an invalid index)
  199. sortedMixesState.Span.Fill(-1);
  200. Memory<byte> nodeStatesWorkBuffer = Memory<byte>.Empty;
  201. Memory<byte> edgeMatrixWorkBuffer = Memory<byte>.Empty;
  202. if (_behaviourContext.IsSplitterSupported())
  203. {
  204. nodeStatesWorkBuffer = workBufferAllocator.Allocate((uint)NodeStates.GetWorkBufferSize((int)mixesCount), 1);
  205. edgeMatrixWorkBuffer = workBufferAllocator.Allocate((uint)EdgeMatrix.GetWorkBufferSize((int)mixesCount), 1);
  206. if (nodeStatesWorkBuffer.IsEmpty || edgeMatrixWorkBuffer.IsEmpty)
  207. {
  208. return ResultCode.WorkBufferTooSmall;
  209. }
  210. }
  211. _mixContext.Initialize(sortedMixesState, mixes, nodeStatesWorkBuffer, edgeMatrixWorkBuffer);
  212. _memoryPools = workBufferAllocator.Allocate<MemoryPoolState>(_memoryPoolCount, MemoryPoolState.Alignment);
  213. if (_memoryPools.IsEmpty)
  214. {
  215. return ResultCode.WorkBufferTooSmall;
  216. }
  217. foreach (ref MemoryPoolState state in _memoryPools.Span)
  218. {
  219. state = MemoryPoolState.Create(MemoryPoolState.LocationType.Cpu);
  220. }
  221. if (!_splitterContext.Initialize(ref _behaviourContext, ref parameter, workBufferAllocator))
  222. {
  223. return ResultCode.WorkBufferTooSmall;
  224. }
  225. _processHandle = processHandle;
  226. _upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount);
  227. _effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0);
  228. _sinkContext.Initialize(parameter.SinkCount);
  229. Memory<VoiceUpdateState> voiceUpdateStatesDsp = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align);
  230. if (voiceUpdateStatesDsp.IsEmpty)
  231. {
  232. return ResultCode.WorkBufferTooSmall;
  233. }
  234. _voiceContext.Initialize(sortedVoices, voices, voiceChannelResources, voiceUpdateStates, voiceUpdateStatesDsp, parameter.VoiceCount);
  235. if (parameter.PerformanceMetricFramesCount > 0)
  236. {
  237. ulong performanceBufferSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref _behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
  238. _performanceBuffer = workBufferAllocator.Allocate(performanceBufferSize, Constants.BufferAlignment);
  239. if (_performanceBuffer.IsEmpty)
  240. {
  241. return ResultCode.WorkBufferTooSmall;
  242. }
  243. _performanceManager = PerformanceManager.Create(_performanceBuffer, ref parameter, _behaviourContext);
  244. }
  245. else
  246. {
  247. _performanceManager = null;
  248. }
  249. _totalElapsedTicksUpdating = 0;
  250. _totalElapsedTicks = 0;
  251. _renderingTimeLimitPercent = 100;
  252. _voiceDropEnabled = parameter.VoiceDropEnabled && _executionMode == AudioRendererExecutionMode.Auto;
  253. AudioProcessorMemoryManager.InvalidateDataCache(workBuffer, workBufferSize);
  254. _processHandle = processHandle;
  255. _elapsedFrameCount = 0;
  256. switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion())
  257. {
  258. case 1:
  259. _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion1(_sampleCount, _mixBufferCount);
  260. break;
  261. case 2:
  262. _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion2(_sampleCount, _mixBufferCount);
  263. break;
  264. case 3:
  265. _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion3(_sampleCount, _mixBufferCount);
  266. break;
  267. case 4:
  268. _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount);
  269. break;
  270. case 5:
  271. _commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount);
  272. break;
  273. default:
  274. throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}.");
  275. }
  276. return ResultCode.Success;
  277. }
  278. public void Start()
  279. {
  280. Logger.Info?.Print(LogClass.AudioRenderer, $"Starting renderer id {_sessionId}");
  281. lock (_lock)
  282. {
  283. _elapsedFrameCount = 0;
  284. _isActive = true;
  285. }
  286. }
  287. public void Stop()
  288. {
  289. Logger.Info?.Print(LogClass.AudioRenderer, $"Stopping renderer id {_sessionId}");
  290. lock (_lock)
  291. {
  292. _isActive = false;
  293. }
  294. if (_executionMode == AudioRendererExecutionMode.Auto)
  295. {
  296. _terminationEvent.WaitOne();
  297. }
  298. Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}");
  299. }
  300. public void Disable()
  301. {
  302. lock (_lock)
  303. {
  304. _isActive = false;
  305. }
  306. }
  307. public ResultCode Update(Memory<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input)
  308. {
  309. lock (_lock)
  310. {
  311. ulong updateStartTicks = GetSystemTicks();
  312. output.Span.Fill(0);
  313. StateUpdater stateUpdater = new StateUpdater(input, output, _processHandle, _behaviourContext);
  314. ResultCode result;
  315. result = stateUpdater.UpdateBehaviourContext();
  316. if (result != ResultCode.Success)
  317. {
  318. return result;
  319. }
  320. result = stateUpdater.UpdateMemoryPools(_memoryPools.Span);
  321. if (result != ResultCode.Success)
  322. {
  323. return result;
  324. }
  325. result = stateUpdater.UpdateVoiceChannelResources(_voiceContext);
  326. if (result != ResultCode.Success)
  327. {
  328. return result;
  329. }
  330. result = stateUpdater.UpdateVoices(_voiceContext, _memoryPools);
  331. if (result != ResultCode.Success)
  332. {
  333. return result;
  334. }
  335. result = stateUpdater.UpdateEffects(_effectContext, _isActive, _memoryPools);
  336. if (result != ResultCode.Success)
  337. {
  338. return result;
  339. }
  340. if (_behaviourContext.IsSplitterSupported())
  341. {
  342. result = stateUpdater.UpdateSplitter(_splitterContext);
  343. if (result != ResultCode.Success)
  344. {
  345. return result;
  346. }
  347. }
  348. result = stateUpdater.UpdateMixes(_mixContext, GetMixBufferCount(), _effectContext, _splitterContext);
  349. if (result != ResultCode.Success)
  350. {
  351. return result;
  352. }
  353. result = stateUpdater.UpdateSinks(_sinkContext, _memoryPools);
  354. if (result != ResultCode.Success)
  355. {
  356. return result;
  357. }
  358. result = stateUpdater.UpdatePerformanceBuffer(_performanceManager, performanceOutput.Span);
  359. if (result != ResultCode.Success)
  360. {
  361. return result;
  362. }
  363. result = stateUpdater.UpdateErrorInfo();
  364. if (result != ResultCode.Success)
  365. {
  366. return result;
  367. }
  368. if (_behaviourContext.IsElapsedFrameCountSupported())
  369. {
  370. result = stateUpdater.UpdateRendererInfo(_elapsedFrameCount);
  371. if (result != ResultCode.Success)
  372. {
  373. return result;
  374. }
  375. }
  376. result = stateUpdater.CheckConsumedSize();
  377. if (result != ResultCode.Success)
  378. {
  379. return result;
  380. }
  381. _systemEvent.Clear();
  382. ulong updateEndTicks = GetSystemTicks();
  383. _totalElapsedTicksUpdating += (updateEndTicks - updateStartTicks);
  384. return result;
  385. }
  386. }
  387. private ulong GetSystemTicks()
  388. {
  389. return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
  390. }
  391. private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
  392. {
  393. int i;
  394. for (i = 0; i < commandBuffer.CommandList.Commands.Count; i++)
  395. {
  396. ICommand command = commandBuffer.CommandList.Commands[i];
  397. CommandType commandType = command.CommandType;
  398. if (commandType == CommandType.AdpcmDataSourceVersion1 ||
  399. commandType == CommandType.AdpcmDataSourceVersion2 ||
  400. commandType == CommandType.PcmInt16DataSourceVersion1 ||
  401. commandType == CommandType.PcmInt16DataSourceVersion2 ||
  402. commandType == CommandType.PcmFloatDataSourceVersion1 ||
  403. commandType == CommandType.PcmFloatDataSourceVersion2 ||
  404. commandType == CommandType.Performance)
  405. {
  406. break;
  407. }
  408. }
  409. uint voiceDropped = 0;
  410. for (; i < commandBuffer.CommandList.Commands.Count; i++)
  411. {
  412. ICommand targetCommand = commandBuffer.CommandList.Commands[i];
  413. int targetNodeId = targetCommand.NodeId;
  414. if (voicesEstimatedTime <= deltaTimeDsp || NodeIdHelper.GetType(targetNodeId) != NodeIdType.Voice)
  415. {
  416. break;
  417. }
  418. ref VoiceState voice = ref _voiceContext.GetState(NodeIdHelper.GetBase(targetNodeId));
  419. if (voice.Priority == Constants.VoiceHighestPriority)
  420. {
  421. break;
  422. }
  423. // We can safely drop this voice, disable all associated commands while activating depop preparation commands.
  424. voiceDropped++;
  425. voice.VoiceDropFlag = true;
  426. Logger.Warning?.Print(LogClass.AudioRenderer, $"Dropping voice {voice.NodeId}");
  427. for (; i < commandBuffer.CommandList.Commands.Count; i++)
  428. {
  429. ICommand command = commandBuffer.CommandList.Commands[i];
  430. if (command.NodeId != targetNodeId)
  431. {
  432. break;
  433. }
  434. if (command.CommandType == CommandType.DepopPrepare)
  435. {
  436. command.Enabled = true;
  437. }
  438. else if (command.CommandType == CommandType.Performance || !command.Enabled)
  439. {
  440. continue;
  441. }
  442. else
  443. {
  444. command.Enabled = false;
  445. voicesEstimatedTime -= (long)command.EstimatedProcessingTime;
  446. }
  447. }
  448. }
  449. return voiceDropped;
  450. }
  451. private void GenerateCommandList(out CommandList commandList)
  452. {
  453. Debug.Assert(_executionMode == AudioRendererExecutionMode.Auto);
  454. PoolMapper.ClearUsageState(_memoryPools);
  455. ulong startTicks = GetSystemTicks();
  456. commandList = new CommandList(this);
  457. if (_performanceManager != null)
  458. {
  459. _performanceManager.TapFrame(_isDspRunningBehind, _voiceDropCount, _renderingStartTick);
  460. _isDspRunningBehind = false;
  461. _voiceDropCount = 0;
  462. _renderingStartTick = 0;
  463. }
  464. CommandBuffer commandBuffer = new CommandBuffer(commandList, _commandProcessingTimeEstimator);
  465. CommandGenerator commandGenerator = new CommandGenerator(commandBuffer, GetContext(), _voiceContext, _mixContext, _effectContext, _sinkContext, _splitterContext, _performanceManager);
  466. _voiceContext.Sort();
  467. commandGenerator.GenerateVoices();
  468. long voicesEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
  469. commandGenerator.GenerateSubMixes();
  470. commandGenerator.GenerateFinalMixes();
  471. commandGenerator.GenerateSinks();
  472. long totalEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
  473. if (_voiceDropEnabled)
  474. {
  475. long maxDspTime = GetMaxAllocatedTimeForDsp();
  476. long restEstimateTime = totalEstimatedTime - voicesEstimatedTime;
  477. long deltaTimeDsp = Math.Max(maxDspTime - restEstimateTime, 0);
  478. _voiceDropCount = ComputeVoiceDrop(commandBuffer, voicesEstimatedTime, deltaTimeDsp);
  479. }
  480. _voiceContext.UpdateForCommandGeneration();
  481. if (_behaviourContext.IsEffectInfoVersion2Supported())
  482. {
  483. _effectContext.UpdateResultStateForCommandGeneration();
  484. }
  485. ulong endTicks = GetSystemTicks();
  486. _totalElapsedTicks = endTicks - startTicks;
  487. _renderingStartTick = GetSystemTicks();
  488. _elapsedFrameCount++;
  489. }
  490. private int GetMaxAllocatedTimeForDsp()
  491. {
  492. return (int)(Constants.AudioProcessorMaxUpdateTimePerSessions * _behaviourContext.GetAudioRendererProcessingTimeLimit() * (GetRenderingTimeLimit() / 100.0f));
  493. }
  494. public void SendCommands()
  495. {
  496. lock (_lock)
  497. {
  498. if (_isActive)
  499. {
  500. _terminationEvent.Reset();
  501. GenerateCommandList(out CommandList commands);
  502. _manager.Processor.Send(_sessionId,
  503. commands,
  504. GetMaxAllocatedTimeForDsp(),
  505. _appletResourceId);
  506. _systemEvent.Signal();
  507. }
  508. else
  509. {
  510. _terminationEvent.Set();
  511. }
  512. }
  513. }
  514. public uint GetMixBufferCount()
  515. {
  516. return _mixBufferCount;
  517. }
  518. public void SetRenderingTimeLimitPercent(uint percent)
  519. {
  520. Debug.Assert(percent <= 100);
  521. _renderingTimeLimitPercent = percent;
  522. }
  523. public uint GetRenderingTimeLimit()
  524. {
  525. return _renderingTimeLimitPercent;
  526. }
  527. public Memory<float> GetMixBuffer()
  528. {
  529. return _mixBuffer;
  530. }
  531. public uint GetSampleCount()
  532. {
  533. return _sampleCount;
  534. }
  535. public uint GetSampleRate()
  536. {
  537. return _sampleRate;
  538. }
  539. public uint GetVoiceChannelCountMax()
  540. {
  541. return _voiceChannelCountMax;
  542. }
  543. public bool IsActive()
  544. {
  545. return _isActive;
  546. }
  547. private RendererSystemContext GetContext()
  548. {
  549. return new RendererSystemContext
  550. {
  551. ChannelCount = _manager.Processor.OutputDevices[_sessionId].GetChannelCount(),
  552. BehaviourContext = _behaviourContext,
  553. DepopBuffer = _depopBuffer,
  554. MixBufferCount = GetMixBufferCount(),
  555. SessionId = _sessionId,
  556. UpsamplerManager = _upsamplerManager
  557. };
  558. }
  559. public int GetSessionId()
  560. {
  561. return _sessionId;
  562. }
  563. public static ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter)
  564. {
  565. BehaviourContext behaviourContext = new BehaviourContext();
  566. behaviourContext.SetUserRevision(parameter.Revision);
  567. uint mixesCount = parameter.SubMixBufferCount + 1;
  568. uint memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
  569. ulong size = 0;
  570. // Mix Buffers
  571. size = WorkBufferAllocator.GetTargetSize<float>(size, parameter.SampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount), 0x10);
  572. // Upsampler workbuffer
  573. size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
  574. // Depop buffer
  575. size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
  576. // Voice
  577. size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
  578. size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.VoiceCount, 0x10);
  579. size = WorkBufferAllocator.GetTargetSize<VoiceChannelResource>(size, parameter.VoiceCount, VoiceChannelResource.Alignment);
  580. size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);
  581. // Mix
  582. size = WorkBufferAllocator.GetTargetSize<MixState>(size, mixesCount, MixState.Alignment);
  583. size = WorkBufferAllocator.GetTargetSize<int>(size, parameter.EffectCount * mixesCount, 0x10);
  584. size = WorkBufferAllocator.GetTargetSize<int>(size, mixesCount, 0x10);
  585. if (behaviourContext.IsSplitterSupported())
  586. {
  587. size += (ulong)BitUtils.AlignUp(NodeStates.GetWorkBufferSize((int)mixesCount) + EdgeMatrix.GetWorkBufferSize((int)mixesCount), 0x10);
  588. }
  589. // Memory Pool
  590. size = WorkBufferAllocator.GetTargetSize<MemoryPoolState>(size, memoryPoolCount, MemoryPoolState.Alignment);
  591. // Splitter
  592. size = SplitterContext.GetWorkBufferSize(size, ref behaviourContext, ref parameter);
  593. // DSP Voice
  594. size = WorkBufferAllocator.GetTargetSize<VoiceUpdateState>(size, parameter.VoiceCount, VoiceUpdateState.Align);
  595. // Performance
  596. if (parameter.PerformanceMetricFramesCount > 0)
  597. {
  598. ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
  599. size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
  600. }
  601. return BitUtils.AlignUp(size, Constants.WorkBufferAlignment);
  602. }
  603. public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)
  604. {
  605. systemEvent = default;
  606. if (_executionMode == AudioRendererExecutionMode.Manual)
  607. {
  608. return ResultCode.UnsupportedOperation;
  609. }
  610. systemEvent = _systemEvent;
  611. return ResultCode.Success;
  612. }
  613. public void Dispose()
  614. {
  615. if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
  616. {
  617. Dispose(true);
  618. }
  619. }
  620. protected virtual void Dispose(bool disposing)
  621. {
  622. if (disposing)
  623. {
  624. if (_isActive)
  625. {
  626. Stop();
  627. }
  628. PoolMapper mapper = new PoolMapper(_processHandle, false);
  629. mapper.Unmap(ref _dspMemoryPoolState);
  630. PoolMapper.ClearUsageState(_memoryPools);
  631. for (int i = 0; i < _memoryPoolCount; i++)
  632. {
  633. ref MemoryPoolState memoryPool = ref _memoryPools.Span[i];
  634. if (memoryPool.IsMapped())
  635. {
  636. mapper.Unmap(ref memoryPool);
  637. }
  638. }
  639. _manager.Unregister(this);
  640. _terminationEvent.Dispose();
  641. _workBufferMemoryPin.Dispose();
  642. _workBufferRegion.Dispose();
  643. if (MemoryManager is IRefCounted rc)
  644. {
  645. rc.DecrementReferenceCount();
  646. MemoryManager = null;
  647. }
  648. }
  649. }
  650. }
  651. }