summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/system.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/renderer/system.cpp')
-rw-r--r--src/audio_core/renderer/system.cpp802
1 files changed, 802 insertions, 0 deletions
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
new file mode 100644
index 000000000..7a217969e
--- /dev/null
+++ b/src/audio_core/renderer/system.cpp
@@ -0,0 +1,802 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <span>
6
7#include "audio_core/audio_core.h"
8#include "audio_core/common/audio_renderer_parameter.h"
9#include "audio_core/common/common.h"
10#include "audio_core/common/feature_support.h"
11#include "audio_core/common/workbuffer_allocator.h"
12#include "audio_core/renderer/adsp/adsp.h"
13#include "audio_core/renderer/behavior/info_updater.h"
14#include "audio_core/renderer/command/command_buffer.h"
15#include "audio_core/renderer/command/command_generator.h"
16#include "audio_core/renderer/command/command_list_header.h"
17#include "audio_core/renderer/effect/effect_info_base.h"
18#include "audio_core/renderer/effect/effect_result_state.h"
19#include "audio_core/renderer/memory/memory_pool_info.h"
20#include "audio_core/renderer/memory/pool_mapper.h"
21#include "audio_core/renderer/mix/mix_info.h"
22#include "audio_core/renderer/nodes/edge_matrix.h"
23#include "audio_core/renderer/nodes/node_states.h"
24#include "audio_core/renderer/sink/sink_info_base.h"
25#include "audio_core/renderer/system.h"
26#include "audio_core/renderer/upsampler/upsampler_info.h"
27#include "audio_core/renderer/voice/voice_channel_resource.h"
28#include "audio_core/renderer/voice/voice_info.h"
29#include "audio_core/renderer/voice/voice_state.h"
30#include "common/alignment.h"
31#include "core/core.h"
32#include "core/core_timing.h"
33#include "core/hle/kernel/k_event.h"
34#include "core/hle/kernel/k_transfer_memory.h"
35#include "core/memory.h"
36
37namespace AudioCore::AudioRenderer {
38
39u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
40 BehaviorInfo behavior;
41 behavior.SetUserLibRevision(params.revision);
42
43 u64 size{0};
44
45 size += Common::AlignUp(params.mixes * sizeof(s32), 0x40);
46 size += params.sub_mixes * MaxEffects * sizeof(s32);
47 size += (params.sub_mixes + 1) * sizeof(MixInfo);
48 size += params.voices * (sizeof(VoiceInfo) + sizeof(VoiceChannelResource) + sizeof(VoiceState));
49 size += Common::AlignUp((params.sub_mixes + 1) * sizeof(MixInfo*), 0x10);
50 size += Common::AlignUp(params.voices * sizeof(VoiceInfo*), 0x10);
51 size += Common::AlignUp(((params.sinks + params.sub_mixes) * TargetSampleCount * sizeof(s32) +
52 params.sample_count * sizeof(s32)) *
53 (params.mixes + MaxChannels),
54 0x40);
55
56 if (behavior.IsSplitterSupported()) {
57 const auto node_size{NodeStates::GetWorkBufferSize(params.sub_mixes + 1)};
58 const auto edge_size{EdgeMatrix::GetWorkBufferSize(params.sub_mixes + 1)};
59 size += Common::AlignUp(node_size + edge_size, 0x10);
60 }
61
62 size += SplitterContext::CalcWorkBufferSize(behavior, params);
63 size += (params.effects + params.voices * MaxWaveBuffers) * sizeof(MemoryPoolInfo);
64
65 if (behavior.IsEffectInfoVersion2Supported()) {
66 size += params.effects * sizeof(EffectResultState);
67 }
68 size += 0x50;
69
70 size = Common::AlignUp(size, 0x40);
71
72 size += (params.sinks + params.sub_mixes) * sizeof(UpsamplerInfo);
73 size += params.effects * sizeof(EffectInfoBase);
74 size += Common::AlignUp(params.voices * sizeof(VoiceState), 0x40);
75 size += params.sinks * sizeof(SinkInfoBase);
76
77 if (behavior.IsEffectInfoVersion2Supported()) {
78 size += params.effects * sizeof(EffectResultState);
79 }
80
81 if (params.perf_frames > 0) {
82 auto perf_size{PerformanceManager::GetRequiredBufferSizeForPerformanceMetricsPerFrame(
83 behavior, params)};
84 size += Common::AlignUp(perf_size * (params.perf_frames + 1) + 0xC0, 0x100);
85 }
86
87 if (behavior.IsVariadicCommandBufferSizeSupported()) {
88 size += CommandGenerator::CalculateCommandBufferSize(behavior, params) + (0x40 - 1) * 2;
89 } else {
90 size += 0x18000 + (0x40 - 1) * 2;
91 }
92
93 size = Common::AlignUp(size, 0x1000);
94 return size;
95}
96
97System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
98 : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
99
100Result System::Initialize(const AudioRendererParameterInternal& params,
101 Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size,
102 const u32 process_handle_, const u64 applet_resource_user_id_,
103 const s32 session_id_) {
104 if (!CheckValidRevision(params.revision)) {
105 return Service::Audio::ERR_INVALID_REVISION;
106 }
107
108 if (GetWorkBufferSize(params) > transfer_memory_size) {
109 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
110 }
111
112 if (process_handle_ == 0) {
113 return Service::Audio::ERR_INVALID_PROCESS_HANDLE;
114 }
115
116 behavior.SetUserLibRevision(params.revision);
117
118 process_handle = process_handle_;
119 applet_resource_user_id = applet_resource_user_id_;
120 session_id = session_id_;
121
122 sample_rate = params.sample_rate;
123 sample_count = params.sample_count;
124 mix_buffer_count = static_cast<s16>(params.mixes);
125 voice_channels = MaxChannels;
126 upsampler_count = params.sinks + params.sub_mixes;
127 memory_pool_count = params.effects + params.voices * MaxWaveBuffers;
128 render_device = params.rendering_device;
129 execution_mode = params.execution_mode;
130
131 core.Memory().ZeroBlock(*core.Kernel().CurrentProcess(), transfer_memory->GetSourceAddress(),
132 transfer_memory_size);
133
134 // Note: We're not actually using the transfer memory because it's a pain to code for.
135 // Allocate the memory normally instead and hope the game doesn't try to read anything back
136 workbuffer = std::make_unique<u8[]>(transfer_memory_size);
137 workbuffer_size = transfer_memory_size;
138
139 PoolMapper pool_mapper(process_handle, false);
140 pool_mapper.InitializeSystemPool(memory_pool_info, workbuffer.get(), workbuffer_size);
141
142 WorkbufferAllocator allocator({workbuffer.get(), workbuffer_size}, workbuffer_size);
143
144 samples_workbuffer =
145 allocator.Allocate<s32>((voice_channels + mix_buffer_count) * sample_count, 0x10);
146 if (samples_workbuffer.empty()) {
147 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
148 }
149
150 auto upsampler_workbuffer{allocator.Allocate<s32>(
151 (voice_channels + mix_buffer_count) * TargetSampleCount * upsampler_count, 0x10)};
152 if (upsampler_workbuffer.empty()) {
153 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
154 }
155
156 depop_buffer =
157 allocator.Allocate<s32>(Common::AlignUp(static_cast<u32>(mix_buffer_count), 0x40), 0x40);
158 if (depop_buffer.empty()) {
159 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
160 }
161
162 // invalidate samples_workbuffer DSP cache
163
164 auto voice_infos{allocator.Allocate<VoiceInfo>(params.voices, 0x10)};
165 for (auto& voice_info : voice_infos) {
166 std::construct_at<VoiceInfo>(&voice_info);
167 }
168
169 if (voice_infos.empty()) {
170 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
171 }
172
173 auto sorted_voice_infos{allocator.Allocate<VoiceInfo*>(params.voices, 0x10)};
174 if (sorted_voice_infos.empty()) {
175 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
176 }
177
178 std::memset(sorted_voice_infos.data(), 0, sorted_voice_infos.size_bytes());
179
180 auto voice_channel_resources{allocator.Allocate<VoiceChannelResource>(params.voices, 0x10)};
181 u32 i{0};
182 for (auto& voice_channel_resource : voice_channel_resources) {
183 std::construct_at<VoiceChannelResource>(&voice_channel_resource, i++);
184 }
185
186 if (voice_channel_resources.empty()) {
187 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
188 }
189
190 auto voice_cpu_states{allocator.Allocate<VoiceState>(params.voices, 0x10)};
191 if (voice_cpu_states.empty()) {
192 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
193 }
194
195 for (auto& voice_state : voice_cpu_states) {
196 voice_state = {};
197 }
198
199 auto mix_infos{allocator.Allocate<MixInfo>(params.sub_mixes + 1, 0x10)};
200
201 if (mix_infos.empty()) {
202 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
203 }
204
205 u32 effect_process_order_count{0};
206 std::span<s32> effect_process_order_buffer{};
207
208 if (params.effects > 0) {
209 effect_process_order_count = params.effects * (params.sub_mixes + 1);
210 effect_process_order_buffer = allocator.Allocate<s32>(effect_process_order_count, 0x10);
211 if (effect_process_order_buffer.empty()) {
212 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
213 }
214 }
215
216 i = 0;
217 for (auto& mix_info : mix_infos) {
218 std::construct_at<MixInfo>(
219 &mix_info, effect_process_order_buffer.subspan(i * params.effects, params.effects),
220 params.effects, this->behavior);
221 i++;
222 }
223
224 auto sorted_mix_infos{allocator.Allocate<MixInfo*>(params.sub_mixes + 1, 0x10)};
225 if (sorted_mix_infos.empty()) {
226 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
227 }
228
229 std::memset(sorted_mix_infos.data(), 0, sorted_mix_infos.size_bytes());
230
231 if (behavior.IsSplitterSupported()) {
232 u64 node_state_size{NodeStates::GetWorkBufferSize(params.sub_mixes + 1)};
233 u64 edge_matrix_size{EdgeMatrix::GetWorkBufferSize(params.sub_mixes + 1)};
234
235 auto node_states_workbuffer{allocator.Allocate<u8>(node_state_size, 1)};
236 auto edge_matrix_workbuffer{allocator.Allocate<u8>(edge_matrix_size, 1)};
237
238 if (node_states_workbuffer.empty() || edge_matrix_workbuffer.size() == 0) {
239 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
240 }
241
242 mix_context.Initialize(sorted_mix_infos, mix_infos, params.sub_mixes + 1,
243 effect_process_order_buffer, effect_process_order_count,
244 node_states_workbuffer, node_state_size, edge_matrix_workbuffer,
245 edge_matrix_size);
246 } else {
247 mix_context.Initialize(sorted_mix_infos, mix_infos, params.sub_mixes + 1,
248 effect_process_order_buffer, effect_process_order_count, {}, 0, {},
249 0);
250 }
251
252 upsampler_manager = allocator.Allocate<UpsamplerManager>(1, 0x10).data();
253 if (upsampler_manager == nullptr) {
254 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
255 }
256
257 memory_pool_workbuffer = allocator.Allocate<MemoryPoolInfo>(memory_pool_count, 0x10);
258 for (auto& memory_pool : memory_pool_workbuffer) {
259 std::construct_at<MemoryPoolInfo>(&memory_pool, MemoryPoolInfo::Location::DSP);
260 }
261
262 if (memory_pool_workbuffer.empty() && memory_pool_count > 0) {
263 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
264 }
265
266 if (!splitter_context.Initialize(behavior, params, allocator)) {
267 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
268 }
269
270 std::span<EffectResultState> effect_result_states_cpu{};
271 if (behavior.IsEffectInfoVersion2Supported() && params.effects > 0) {
272 effect_result_states_cpu = allocator.Allocate<EffectResultState>(params.effects, 0x10);
273 if (effect_result_states_cpu.empty()) {
274 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
275 }
276 std::memset(effect_result_states_cpu.data(), 0, effect_result_states_cpu.size_bytes());
277 }
278
279 allocator.Align(0x40);
280
281 unk_2B0 = allocator.GetSize() - allocator.GetCurrentOffset();
282 unk_2A8 = {&workbuffer[allocator.GetCurrentOffset()], unk_2B0};
283
284 upsampler_infos = allocator.Allocate<UpsamplerInfo>(upsampler_count, 0x40);
285 for (auto& upsampler_info : upsampler_infos) {
286 std::construct_at<UpsamplerInfo>(&upsampler_info);
287 }
288
289 std::construct_at<UpsamplerManager>(upsampler_manager, upsampler_count, upsampler_infos,
290 upsampler_workbuffer);
291
292 if (upsampler_infos.empty()) {
293 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
294 }
295
296 auto effect_infos{allocator.Allocate<EffectInfoBase>(params.effects, 0x40)};
297 for (auto& effect_info : effect_infos) {
298 std::construct_at<EffectInfoBase>(&effect_info);
299 }
300
301 if (effect_infos.empty() && params.effects > 0) {
302 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
303 }
304
305 std::span<EffectResultState> effect_result_states_dsp{};
306 if (behavior.IsEffectInfoVersion2Supported() && params.effects > 0) {
307 effect_result_states_dsp = allocator.Allocate<EffectResultState>(params.effects, 0x40);
308 if (effect_result_states_dsp.empty()) {
309 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
310 }
311 std::memset(effect_result_states_dsp.data(), 0, effect_result_states_dsp.size_bytes());
312 }
313
314 effect_context.Initialize(effect_infos, params.effects, effect_result_states_cpu,
315 effect_result_states_dsp, effect_result_states_dsp.size());
316
317 auto sinks{allocator.Allocate<SinkInfoBase>(params.sinks, 0x10)};
318 for (auto& sink : sinks) {
319 std::construct_at<SinkInfoBase>(&sink);
320 }
321
322 if (sinks.empty()) {
323 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
324 }
325
326 sink_context.Initialize(sinks, params.sinks);
327
328 auto voice_dsp_states{allocator.Allocate<VoiceState>(params.voices, 0x40)};
329 if (voice_dsp_states.empty()) {
330 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
331 }
332
333 for (auto& voice_state : voice_dsp_states) {
334 voice_state = {};
335 }
336
337 voice_context.Initialize(sorted_voice_infos, voice_infos, voice_channel_resources,
338 voice_cpu_states, voice_dsp_states, params.voices);
339
340 if (params.perf_frames > 0) {
341 const auto perf_workbuffer_size{
342 PerformanceManager::GetRequiredBufferSizeForPerformanceMetricsPerFrame(behavior,
343 params) *
344 (params.perf_frames + 1) +
345 0xC};
346 performance_workbuffer = allocator.Allocate<u8>(perf_workbuffer_size, 0x40);
347 if (performance_workbuffer.empty()) {
348 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
349 }
350 std::memset(performance_workbuffer.data(), 0, performance_workbuffer.size_bytes());
351 performance_manager.Initialize(performance_workbuffer, performance_workbuffer.size_bytes(),
352 params, behavior, memory_pool_info);
353 }
354
355 render_time_limit_percent = 100;
356 drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
357
358 allocator.Align(0x40);
359 command_workbuffer_size = allocator.GetRemainingSize();
360 command_workbuffer = allocator.Allocate<u8>(command_workbuffer_size, 0x40);
361 if (command_workbuffer.empty()) {
362 return Service::Audio::ERR_INSUFFICIENT_BUFFER_SIZE;
363 }
364
365 command_buffer_size = 0;
366 reset_command_buffers = true;
367
368 // nn::audio::dsp::FlushDataCache(transferMemory, transferMemorySize);
369
370 if (behavior.IsCommandProcessingTimeEstimatorVersion5Supported()) {
371 command_processing_time_estimator =
372 std::make_unique<CommandProcessingTimeEstimatorVersion5>(sample_count,
373 mix_buffer_count);
374 } else if (behavior.IsCommandProcessingTimeEstimatorVersion4Supported()) {
375 command_processing_time_estimator =
376 std::make_unique<CommandProcessingTimeEstimatorVersion4>(sample_count,
377 mix_buffer_count);
378 } else if (behavior.IsCommandProcessingTimeEstimatorVersion3Supported()) {
379 command_processing_time_estimator =
380 std::make_unique<CommandProcessingTimeEstimatorVersion3>(sample_count,
381 mix_buffer_count);
382 } else if (behavior.IsCommandProcessingTimeEstimatorVersion2Supported()) {
383 command_processing_time_estimator =
384 std::make_unique<CommandProcessingTimeEstimatorVersion2>(sample_count,
385 mix_buffer_count);
386 } else {
387 command_processing_time_estimator =
388 std::make_unique<CommandProcessingTimeEstimatorVersion1>(sample_count,
389 mix_buffer_count);
390 }
391
392 initialized = true;
393 return ResultSuccess;
394}
395
396void System::Finalize() {
397 if (!initialized) {
398 return;
399 }
400
401 if (active) {
402 Stop();
403 }
404
405 applet_resource_user_id = 0;
406
407 PoolMapper pool_mapper(process_handle, false);
408 pool_mapper.Unmap(memory_pool_info);
409
410 if (process_handle) {
411 pool_mapper.ClearUseState(memory_pool_workbuffer, memory_pool_count);
412 for (auto& memory_pool : memory_pool_workbuffer) {
413 if (memory_pool.IsMapped()) {
414 pool_mapper.Unmap(memory_pool);
415 }
416 }
417
418 // dsp::ProcessCleanup
419 // close handle
420 }
421 initialized = false;
422}
423
424void System::Start() {
425 std::scoped_lock l{lock};
426 frames_elapsed = 0;
427 state = State::Started;
428 active = true;
429}
430
431void System::Stop() {
432 {
433 std::scoped_lock l{lock};
434 state = State::Stopped;
435 active = false;
436 }
437
438 if (execution_mode == ExecutionMode::Auto) {
439 // Should wait for the system to terminate here, but core timing (should have) already
440 // stopped, so this isn't needed. Find a way to make this definite.
441
442 // terminate_event.Wait();
443 }
444}
445
446Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
447 std::scoped_lock l{lock};
448
449 const auto start_time{core.CoreTiming().GetClockTicks()};
450
451 InfoUpdater info_updater(input, output, process_handle, behavior);
452
453 auto result{info_updater.UpdateBehaviorInfo(behavior)};
454 if (result.IsError()) {
455 LOG_ERROR(Service_Audio, "Failed to update BehaviorInfo!");
456 return result;
457 }
458
459 result = info_updater.UpdateMemoryPools(memory_pool_workbuffer, memory_pool_count);
460 if (result.IsError()) {
461 LOG_ERROR(Service_Audio, "Failed to update MemoryPools!");
462 return result;
463 }
464
465 result = info_updater.UpdateVoiceChannelResources(voice_context);
466 if (result.IsError()) {
467 LOG_ERROR(Service_Audio, "Failed to update VoiceChannelResources!");
468 return result;
469 }
470
471 result = info_updater.UpdateVoices(voice_context, memory_pool_workbuffer, memory_pool_count);
472 if (result.IsError()) {
473 LOG_ERROR(Service_Audio, "Failed to update Voices!");
474 return result;
475 }
476
477 result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
478 memory_pool_count);
479 if (result.IsError()) {
480 LOG_ERROR(Service_Audio, "Failed to update Effects!");
481 return result;
482 }
483
484 if (behavior.IsSplitterSupported()) {
485 result = info_updater.UpdateSplitterInfo(splitter_context);
486 if (result.IsError()) {
487 LOG_ERROR(Service_Audio, "Failed to update SplitterInfo!");
488 return result;
489 }
490 }
491
492 result =
493 info_updater.UpdateMixes(mix_context, mix_buffer_count, effect_context, splitter_context);
494 if (result.IsError()) {
495 LOG_ERROR(Service_Audio, "Failed to update Mixes!");
496 return result;
497 }
498
499 result = info_updater.UpdateSinks(sink_context, memory_pool_workbuffer, memory_pool_count);
500 if (result.IsError()) {
501 LOG_ERROR(Service_Audio, "Failed to update Sinks!");
502 return result;
503 }
504
505 PerformanceManager* perf_manager{nullptr};
506 if (performance_manager.IsInitialized()) {
507 perf_manager = &performance_manager;
508 }
509
510 result =
511 info_updater.UpdatePerformanceBuffer(performance, performance.size_bytes(), perf_manager);
512 if (result.IsError()) {
513 LOG_ERROR(Service_Audio, "Failed to update PerformanceBuffer!");
514 return result;
515 }
516
517 result = info_updater.UpdateErrorInfo(behavior);
518 if (result.IsError()) {
519 LOG_ERROR(Service_Audio, "Failed to update ErrorInfo!");
520 return result;
521 }
522
523 if (behavior.IsElapsedFrameCountSupported()) {
524 result = info_updater.UpdateRendererInfo(frames_elapsed);
525 if (result.IsError()) {
526 LOG_ERROR(Service_Audio, "Failed to update RendererInfo!");
527 return result;
528 }
529 }
530
531 result = info_updater.CheckConsumedSize();
532 if (result.IsError()) {
533 LOG_ERROR(Service_Audio, "Invalid consume size!");
534 return result;
535 }
536
537 adsp_rendered_event->GetWritableEvent().Clear();
538 num_times_updated++;
539
540 const auto end_time{core.CoreTiming().GetClockTicks()};
541 ticks_spent_updating += end_time - start_time;
542
543 return ResultSuccess;
544}
545
546u32 System::GetRenderingTimeLimit() const {
547 return render_time_limit_percent;
548}
549
550void System::SetRenderingTimeLimit(const u32 limit) {
551 render_time_limit_percent = limit;
552}
553
554u32 System::GetSessionId() const {
555 return session_id;
556}
557
558u32 System::GetSampleRate() const {
559 return sample_rate;
560}
561
562u32 System::GetSampleCount() const {
563 return sample_count;
564}
565
566u32 System::GetMixBufferCount() const {
567 return mix_buffer_count;
568}
569
570ExecutionMode System::GetExecutionMode() const {
571 return execution_mode;
572}
573
574u32 System::GetRenderingDevice() const {
575 return render_device;
576}
577
578bool System::IsActive() const {
579 return active;
580}
581
582void System::SendCommandToDsp() {
583 std::scoped_lock l{lock};
584
585 if (initialized) {
586 if (active) {
587 terminate_event.Reset();
588 const auto remaining_command_count{adsp.GetRemainCommandCount(session_id)};
589 u64 command_size{0};
590
591 if (remaining_command_count) {
592 adsp_behind = true;
593 command_size = command_buffer_size;
594 } else {
595 command_size = GenerateCommand(command_workbuffer, command_workbuffer_size);
596 }
597
598 auto translated_addr{
599 memory_pool_info.Translate(CpuAddr(command_workbuffer.data()), command_size)};
600
601 auto time_limit_percent{70.0f};
602 if (behavior.IsAudioRendererProcessingTimeLimit80PercentSupported()) {
603 time_limit_percent = 80.0f;
604 } else if (behavior.IsAudioRendererProcessingTimeLimit75PercentSupported()) {
605 time_limit_percent = 75.0f;
606 } else {
607 // result ignored and 70 is used anyway
608 behavior.IsAudioRendererProcessingTimeLimit70PercentSupported();
609 time_limit_percent = 70.0f;
610 }
611
612 ADSP::CommandBuffer command_buffer{
613 .buffer{translated_addr},
614 .size{command_size},
615 .time_limit{
616 static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
617 (static_cast<f32>(render_time_limit_percent) / 100.0f))},
618 .remaining_command_count{remaining_command_count},
619 .reset_buffers{reset_command_buffers},
620 .applet_resource_user_id{applet_resource_user_id},
621 .render_time_taken{adsp.GetRenderTimeTaken(session_id)},
622 };
623
624 adsp.SendCommandBuffer(session_id, command_buffer);
625 reset_command_buffers = false;
626 command_buffer_size = command_size;
627 if (remaining_command_count == 0) {
628 adsp_rendered_event->GetWritableEvent().Signal();
629 }
630 } else {
631 adsp.ClearRemainCount(session_id);
632 terminate_event.Set();
633 }
634 }
635}
636
637u64 System::GenerateCommand(std::span<u8> in_command_buffer,
638 [[maybe_unused]] const u64 command_buffer_size_) {
639 PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
640 const auto start_time{core.CoreTiming().GetClockTicks()};
641
642 auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())};
643
644 command_list_header->buffer_count = static_cast<s16>(voice_channels + mix_buffer_count);
645 command_list_header->sample_count = sample_count;
646 command_list_header->sample_rate = sample_rate;
647 command_list_header->samples_buffer = samples_workbuffer;
648
649 const auto performance_initialized{performance_manager.IsInitialized()};
650 if (performance_initialized) {
651 performance_manager.TapFrame(adsp_behind, num_voices_dropped, render_start_tick);
652 adsp_behind = false;
653 num_voices_dropped = 0;
654 render_start_tick = 0;
655 }
656
657 s8 channel_count{2};
658 if (execution_mode == ExecutionMode::Auto) {
659 const auto& sink{core.AudioCore().GetOutputSink()};
660 channel_count = static_cast<s8>(sink.GetDeviceChannels());
661 }
662
663 AudioRendererSystemContext render_context{
664 .session_id{session_id},
665 .channels{channel_count},
666 .mix_buffer_count{mix_buffer_count},
667 .behavior{&behavior},
668 .depop_buffer{depop_buffer},
669 .upsampler_manager{upsampler_manager},
670 .memory_pool_info{&memory_pool_info},
671 };
672
673 CommandBuffer command_buffer{
674 .command_list{in_command_buffer},
675 .sample_count{sample_count},
676 .sample_rate{sample_rate},
677 .size{sizeof(CommandListHeader)},
678 .count{0},
679 .estimated_process_time{0},
680 .memory_pool{&memory_pool_info},
681 .time_estimator{command_processing_time_estimator.get()},
682 .behavior{&behavior},
683 };
684
685 PerformanceManager* perf_manager{nullptr};
686 if (performance_initialized) {
687 perf_manager = &performance_manager;
688 }
689
690 CommandGenerator command_generator{command_buffer, *command_list_header, render_context,
691 voice_context, mix_context, effect_context,
692 sink_context, splitter_context, perf_manager};
693
694 voice_context.SortInfo();
695
696 const auto start_estimated_time{command_buffer.estimated_process_time};
697
698 command_generator.GenerateVoiceCommands();
699 command_generator.GenerateSubMixCommands();
700 command_generator.GenerateFinalMixCommands();
701 command_generator.GenerateSinkCommands();
702
703 if (drop_voice) {
704 f32 time_limit_percent{70.0f};
705 if (render_context.behavior->IsAudioRendererProcessingTimeLimit80PercentSupported()) {
706 time_limit_percent = 80.0f;
707 } else if (render_context.behavior
708 ->IsAudioRendererProcessingTimeLimit75PercentSupported()) {
709 time_limit_percent = 75.0f;
710 } else {
711 // result is ignored
712 render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
713 time_limit_percent = 70.0f;
714 }
715 const auto time_limit{static_cast<u32>(
716 static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) +
717 (((time_limit_percent / 100.0f) * 2'880'000.0) *
718 (static_cast<f32>(render_time_limit_percent) / 100.0f)))};
719 num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit);
720 }
721
722 command_list_header->buffer_size = command_buffer.size;
723 command_list_header->command_count = command_buffer.count;
724
725 voice_context.UpdateStateByDspShared();
726
727 if (render_context.behavior->IsEffectInfoVersion2Supported()) {
728 effect_context.UpdateStateByDspShared();
729 }
730
731 const auto end_time{core.CoreTiming().GetClockTicks()};
732 total_ticks_elapsed += end_time - start_time;
733 num_command_lists_generated++;
734 render_start_tick = adsp.GetRenderingStartTick(session_id);
735 frames_elapsed++;
736
737 return command_buffer.size;
738}
739
740u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time,
741 const u32 time_limit) {
742 u32 i{0};
743 auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
744 ICommand* cmd{};
745
746 for (; i < command_buffer.count; i++) {
747 cmd = reinterpret_cast<ICommand*>(command_list);
748 if (cmd->type != CommandId::Performance &&
749 cmd->type != CommandId::DataSourcePcmInt16Version1 &&
750 cmd->type != CommandId::DataSourcePcmInt16Version2 &&
751 cmd->type != CommandId::DataSourcePcmFloatVersion1 &&
752 cmd->type != CommandId::DataSourcePcmFloatVersion2 &&
753 cmd->type != CommandId::DataSourceAdpcmVersion1 &&
754 cmd->type != CommandId::DataSourceAdpcmVersion2) {
755 break;
756 }
757 command_list += cmd->size;
758 }
759
760 if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
761 return 0;
762 }
763
764 auto voices_dropped{0};
765 while (i < command_buffer.count) {
766 const auto node_id{cmd->node_id};
767 const auto node_id_type{cmd->node_id >> 28};
768 const auto node_id_base{cmd->node_id & 0xFFF};
769
770 if (estimated_process_time <= time_limit) {
771 break;
772 }
773
774 if (node_id_type != 1) {
775 break;
776 }
777
778 auto& voice_info{voice_context.GetInfo(node_id_base)};
779 if (voice_info.priority == HighestVoicePriority) {
780 break;
781 }
782
783 voices_dropped++;
784 voice_info.voice_dropped = true;
785
786 if (i < command_buffer.count) {
787 while (cmd->node_id == node_id) {
788 if (cmd->type == CommandId::DepopPrepare) {
789 cmd->enabled = true;
790 } else if (cmd->type == CommandId::Performance || !cmd->enabled) {
791 cmd->enabled = false;
792 }
793 i++;
794 command_list += cmd->size;
795 cmd = reinterpret_cast<ICommand*>(command_list);
796 }
797 }
798 }
799 return voices_dropped;
800}
801
802} // namespace AudioCore::AudioRenderer