diff options
Diffstat (limited to 'src/audio_core/renderer/system.cpp')
| -rw-r--r-- | src/audio_core/renderer/system.cpp | 802 |
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 | |||
| 37 | namespace AudioCore::AudioRenderer { | ||
| 38 | |||
| 39 | u64 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 | |||
| 97 | System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) | ||
| 98 | : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} | ||
| 99 | |||
| 100 | Result 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 | |||
| 396 | void 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 | |||
| 424 | void System::Start() { | ||
| 425 | std::scoped_lock l{lock}; | ||
| 426 | frames_elapsed = 0; | ||
| 427 | state = State::Started; | ||
| 428 | active = true; | ||
| 429 | } | ||
| 430 | |||
| 431 | void 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 | |||
| 446 | Result 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 | |||
| 546 | u32 System::GetRenderingTimeLimit() const { | ||
| 547 | return render_time_limit_percent; | ||
| 548 | } | ||
| 549 | |||
| 550 | void System::SetRenderingTimeLimit(const u32 limit) { | ||
| 551 | render_time_limit_percent = limit; | ||
| 552 | } | ||
| 553 | |||
| 554 | u32 System::GetSessionId() const { | ||
| 555 | return session_id; | ||
| 556 | } | ||
| 557 | |||
| 558 | u32 System::GetSampleRate() const { | ||
| 559 | return sample_rate; | ||
| 560 | } | ||
| 561 | |||
| 562 | u32 System::GetSampleCount() const { | ||
| 563 | return sample_count; | ||
| 564 | } | ||
| 565 | |||
| 566 | u32 System::GetMixBufferCount() const { | ||
| 567 | return mix_buffer_count; | ||
| 568 | } | ||
| 569 | |||
| 570 | ExecutionMode System::GetExecutionMode() const { | ||
| 571 | return execution_mode; | ||
| 572 | } | ||
| 573 | |||
| 574 | u32 System::GetRenderingDevice() const { | ||
| 575 | return render_device; | ||
| 576 | } | ||
| 577 | |||
| 578 | bool System::IsActive() const { | ||
| 579 | return active; | ||
| 580 | } | ||
| 581 | |||
| 582 | void 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 | |||
| 637 | u64 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 | |||
| 740 | u32 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 | ||