AudioRenderSystem.cs 30 KB

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