diff options
| author | 2023-08-31 15:09:15 +0100 | |
|---|---|---|
| committer | 2023-09-04 17:12:16 +0100 | |
| commit | ebd19dec99d9809a669f63294745d7c8facc6d31 (patch) | |
| tree | cd1f34cac0c091c2ffd16c429ac33b8fe133e06e /src/audio_core/adsp | |
| parent | Merge pull request #11420 from t895/long-install-fix (diff) | |
| download | yuzu-ebd19dec99d9809a669f63294745d7c8facc6d31.tar.gz yuzu-ebd19dec99d9809a669f63294745d7c8facc6d31.tar.xz yuzu-ebd19dec99d9809a669f63294745d7c8facc6d31.zip | |
Rework ADSP into a wrapper for apps
Diffstat (limited to 'src/audio_core/adsp')
| -rw-r--r-- | src/audio_core/adsp/adsp.cpp | 18 | ||||
| -rw-r--r-- | src/audio_core/adsp/adsp.h | 50 | ||||
| -rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | 215 | ||||
| -rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | 115 | ||||
| -rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/command_buffer.h | 23 | ||||
| -rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp | 108 | ||||
| -rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/command_list_processor.h | 120 | ||||
| -rw-r--r-- | src/audio_core/adsp/mailbox.h | 69 |
8 files changed, 718 insertions, 0 deletions
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp new file mode 100644 index 000000000..0580990f5 --- /dev/null +++ b/src/audio_core/adsp/adsp.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/adsp.h" | ||
| 5 | #include "core/core.h" | ||
| 6 | |||
| 7 | namespace AudioCore::ADSP { | ||
| 8 | |||
| 9 | ADSP::ADSP(Core::System& system, Sink::Sink& sink) { | ||
| 10 | audio_renderer = | ||
| 11 | std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink); | ||
| 12 | } | ||
| 13 | |||
| 14 | AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { | ||
| 15 | return *audio_renderer.get(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace AudioCore::ADSP | ||
diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h new file mode 100644 index 000000000..bd5bcc63b --- /dev/null +++ b/src/audio_core/adsp/adsp.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } // namespace Core | ||
| 12 | |||
| 13 | namespace AudioCore { | ||
| 14 | namespace Sink { | ||
| 15 | class Sink; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace ADSP { | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Represents the ADSP embedded within the audio sysmodule. | ||
| 22 | * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. | ||
| 23 | * | ||
| 24 | * The kernel will run the apps you write for it, Nintendo have the following: | ||
| 25 | * | ||
| 26 | * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all | ||
| 27 | * audio samples end up, and we skip it entirely, since we have very different backends and | ||
| 28 | * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). | ||
| 29 | * | ||
| 30 | * AudioRenderer - Receives command lists generated by the audio render | ||
| 31 | * system on the host, processes them, and sends the samples to Gmix. | ||
| 32 | * | ||
| 33 | * OpusDecoder - Contains libopus, and decodes Opus audio packets into raw pcm data. | ||
| 34 | * | ||
| 35 | * Communication between the host and ADSP is done through mailboxes, and mapping of shared memory. | ||
| 36 | */ | ||
| 37 | class ADSP { | ||
| 38 | public: | ||
| 39 | explicit ADSP(Core::System& system, Sink::Sink& sink); | ||
| 40 | ~ADSP() = default; | ||
| 41 | |||
| 42 | AudioRenderer::AudioRenderer& AudioRenderer(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | /// AudioRenderer app | ||
| 46 | std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; | ||
| 47 | }; | ||
| 48 | |||
| 49 | } // namespace ADSP | ||
| 50 | } // namespace AudioCore | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp new file mode 100644 index 000000000..3da342ea3 --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 7 | #include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" | ||
| 8 | #include "audio_core/audio_core.h" | ||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/sink/sink.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "common/microprofile.h" | ||
| 13 | #include "common/thread.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_timing.h" | ||
| 16 | |||
| 17 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); | ||
| 18 | |||
| 19 | namespace AudioCore::ADSP::AudioRenderer { | ||
| 20 | |||
| 21 | AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, | ||
| 22 | Sink::Sink& sink_) | ||
| 23 | : system{system_}, memory{memory_}, sink{sink_} {} | ||
| 24 | |||
| 25 | AudioRenderer::~AudioRenderer() { | ||
| 26 | Stop(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void AudioRenderer::Start() { | ||
| 30 | CreateSinkStreams(); | ||
| 31 | |||
| 32 | mailbox.Initialize(AppMailboxId::AudioRenderer); | ||
| 33 | |||
| 34 | main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); | ||
| 35 | |||
| 36 | mailbox.Send(Direction::DSP, {Message::InitializeOK, {}}); | ||
| 37 | if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) { | ||
| 38 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 39 | "message response from ADSP!"); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | running = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | void AudioRenderer::Stop() { | ||
| 46 | if (!running) { | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | |||
| 50 | mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); | ||
| 51 | if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { | ||
| 52 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 53 | "message response from ADSP!"); | ||
| 54 | } | ||
| 55 | main_thread.request_stop(); | ||
| 56 | main_thread.join(); | ||
| 57 | |||
| 58 | for (auto& stream : streams) { | ||
| 59 | if (stream) { | ||
| 60 | stream->Stop(); | ||
| 61 | sink.CloseStream(stream); | ||
| 62 | stream = nullptr; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | running = false; | ||
| 66 | } | ||
| 67 | |||
| 68 | void AudioRenderer::Signal() { | ||
| 69 | signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); | ||
| 70 | Send(Direction::DSP, {Message::Render, {}}); | ||
| 71 | } | ||
| 72 | |||
| 73 | void AudioRenderer::Wait() { | ||
| 74 | auto received = Receive(Direction::Host); | ||
| 75 | if (received.msg != Message::RenderResponse) { | ||
| 76 | LOG_ERROR(Service_Audio, | ||
| 77 | "Did not receive the expected render response from the AudioRenderer! Expected " | ||
| 78 | "{}, got {}", | ||
| 79 | Message::RenderResponse, received.msg); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void AudioRenderer::Send(Direction dir, MailboxMessage message) { | ||
| 84 | mailbox.Send(dir, std::move(message)); | ||
| 85 | } | ||
| 86 | |||
| 87 | MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { | ||
| 88 | return mailbox.Receive(dir, block); | ||
| 89 | } | ||
| 90 | |||
| 91 | void AudioRenderer::SetCommandBuffer(s32 session_id, CommandBuffer& buffer) noexcept { | ||
| 92 | command_buffers[session_id] = buffer; | ||
| 93 | } | ||
| 94 | |||
| 95 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { | ||
| 96 | return command_buffers[session_id].remaining_command_count; | ||
| 97 | } | ||
| 98 | |||
| 99 | void AudioRenderer::ClearRemainCommandCount(s32 session_id) noexcept { | ||
| 100 | command_buffers[session_id].remaining_command_count = 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | u64 AudioRenderer::GetRenderingStartTick(s32 session_id) const noexcept { | ||
| 104 | return (1000 * command_buffers[session_id].render_time_taken_us) + signalled_tick; | ||
| 105 | } | ||
| 106 | |||
| 107 | void AudioRenderer::CreateSinkStreams() { | ||
| 108 | u32 channels{sink.GetDeviceChannels()}; | ||
| 109 | for (u32 i = 0; i < MaxRendererSessions; i++) { | ||
| 110 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; | ||
| 111 | streams[i] = | ||
| 112 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); | ||
| 113 | streams[i]->SetRingSize(4); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void AudioRenderer::Main(std::stop_token stop_token) { | ||
| 118 | static constexpr char name[]{"AudioRenderer"}; | ||
| 119 | MicroProfileOnThreadCreate(name); | ||
| 120 | Common::SetCurrentThreadName(name); | ||
| 121 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | ||
| 122 | |||
| 123 | // TODO: Create buffer map/unmap thread + mailbox | ||
| 124 | // TODO: Create gMix devices, initialize them here | ||
| 125 | |||
| 126 | if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { | ||
| 127 | LOG_ERROR(Service_Audio, | ||
| 128 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | mailbox.Send(Direction::Host, {Message::InitializeOK, {}}); | ||
| 133 | |||
| 134 | // 0.12 seconds (2,304,000 / 19,200,000) | ||
| 135 | constexpr u64 max_process_time{2'304'000ULL}; | ||
| 136 | |||
| 137 | while (!stop_token.stop_requested()) { | ||
| 138 | auto received{mailbox.Receive(Direction::DSP)}; | ||
| 139 | switch (received.msg) { | ||
| 140 | case Message::Shutdown: | ||
| 141 | mailbox.Send(Direction::Host, {Message::Shutdown, {}}); | ||
| 142 | return; | ||
| 143 | |||
| 144 | case Message::Render: { | ||
| 145 | if (system.IsShuttingDown()) [[unlikely]] { | ||
| 146 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); | ||
| 147 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | ||
| 148 | continue; | ||
| 149 | } | ||
| 150 | std::array<bool, MaxRendererSessions> buffers_reset{}; | ||
| 151 | std::array<u64, MaxRendererSessions> render_times_taken{}; | ||
| 152 | const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()}; | ||
| 153 | |||
| 154 | for (u32 index = 0; index < MaxRendererSessions; index++) { | ||
| 155 | auto& command_buffer{command_buffers[index]}; | ||
| 156 | auto& command_list_processor{command_list_processors[index]}; | ||
| 157 | |||
| 158 | // Check this buffer is valid, as it may not be used. | ||
| 159 | if (command_buffer.buffer != 0) { | ||
| 160 | // If there are no remaining commands (from the previous list), | ||
| 161 | // this is a new command list, initialize it. | ||
| 162 | if (command_buffer.remaining_command_count == 0) { | ||
| 163 | command_list_processor.Initialize(system, command_buffer.buffer, | ||
| 164 | command_buffer.size, streams[index]); | ||
| 165 | } | ||
| 166 | |||
| 167 | if (command_buffer.reset_buffer && !buffers_reset[index]) { | ||
| 168 | streams[index]->ClearQueue(); | ||
| 169 | buffers_reset[index] = true; | ||
| 170 | } | ||
| 171 | |||
| 172 | u64 max_time{max_process_time}; | ||
| 173 | if (index == 1 && command_buffer.applet_resource_user_id == | ||
| 174 | command_buffers[0].applet_resource_user_id) { | ||
| 175 | max_time = max_process_time - render_times_taken[0]; | ||
| 176 | if (render_times_taken[0] > max_process_time) { | ||
| 177 | max_time = 0; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | max_time = std::min(command_buffer.time_limit, max_time); | ||
| 182 | command_list_processor.SetProcessTimeMax(max_time); | ||
| 183 | |||
| 184 | if (index == 0) { | ||
| 185 | streams[index]->WaitFreeSpace(stop_token); | ||
| 186 | } | ||
| 187 | |||
| 188 | // Process the command list | ||
| 189 | { | ||
| 190 | MICROPROFILE_SCOPE(Audio_Renderer); | ||
| 191 | render_times_taken[index] = | ||
| 192 | command_list_processor.Process(index) - start_time; | ||
| 193 | } | ||
| 194 | |||
| 195 | const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()}; | ||
| 196 | |||
| 197 | command_buffer.remaining_command_count = | ||
| 198 | command_list_processor.GetRemainingCommandCount(); | ||
| 199 | command_buffer.render_time_taken_us = end_time - start_time; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); | ||
| 204 | } break; | ||
| 205 | |||
| 206 | default: | ||
| 207 | LOG_WARNING(Service_Audio, | ||
| 208 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", | ||
| 209 | received.msg); | ||
| 210 | break; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | } // namespace AudioCore::ADSP::AudioRenderer | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h new file mode 100644 index 000000000..b225e10fb --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <memory> | ||
| 8 | #include <thread> | ||
| 9 | |||
| 10 | #include "audio_core/adsp/apps/audio_renderer/command_buffer.h" | ||
| 11 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" | ||
| 12 | #include "audio_core/adsp/mailbox.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/polyfill_thread.h" | ||
| 15 | #include "common/reader_writer_queue.h" | ||
| 16 | #include "common/thread.h" | ||
| 17 | |||
| 18 | namespace Core { | ||
| 19 | class System; | ||
| 20 | namespace Timing { | ||
| 21 | struct EventType; | ||
| 22 | } | ||
| 23 | namespace Memory { | ||
| 24 | class Memory; | ||
| 25 | } | ||
| 26 | class System; | ||
| 27 | } // namespace Core | ||
| 28 | |||
| 29 | namespace AudioCore { | ||
| 30 | namespace Sink { | ||
| 31 | class Sink; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace ADSP::AudioRenderer { | ||
| 35 | |||
| 36 | enum Message : u32 { | ||
| 37 | Invalid = 0x00, | ||
| 38 | MapUnmap_Map = 0x01, | ||
| 39 | MapUnmap_MapResponse = 0x02, | ||
| 40 | MapUnmap_Unmap = 0x03, | ||
| 41 | MapUnmap_UnmapResponse = 0x04, | ||
| 42 | MapUnmap_InvalidateCache = 0x05, | ||
| 43 | MapUnmap_InvalidateCacheResponse = 0x06, | ||
| 44 | MapUnmap_Shutdown = 0x07, | ||
| 45 | MapUnmap_ShutdownResponse = 0x08, | ||
| 46 | InitializeOK = 0x16, | ||
| 47 | RenderResponse = 0x20, | ||
| 48 | Render = 0x2A, | ||
| 49 | Shutdown = 0x34, | ||
| 50 | }; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * The AudioRenderer application running on the ADSP. | ||
| 54 | */ | ||
| 55 | class AudioRenderer { | ||
| 56 | public: | ||
| 57 | explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); | ||
| 58 | ~AudioRenderer(); | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Start the AudioRenderer. | ||
| 62 | * | ||
| 63 | * @param mailbox The mailbox to use for this session. | ||
| 64 | */ | ||
| 65 | void Start(); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Stop the AudioRenderer. | ||
| 69 | */ | ||
| 70 | void Stop(); | ||
| 71 | |||
| 72 | void Signal(); | ||
| 73 | void Wait(); | ||
| 74 | |||
| 75 | void Send(Direction dir, MailboxMessage message); | ||
| 76 | MailboxMessage Receive(Direction dir, bool block = true); | ||
| 77 | |||
| 78 | void SetCommandBuffer(s32 session_id, CommandBuffer& buffer) noexcept; | ||
| 79 | u32 GetRemainCommandCount(s32 session_id) const noexcept; | ||
| 80 | void ClearRemainCommandCount(s32 session_id) noexcept; | ||
| 81 | u64 GetRenderingStartTick(s32 session_id) const noexcept; | ||
| 82 | |||
| 83 | private: | ||
| 84 | /** | ||
| 85 | * Main AudioRenderer thread, responsible for processing the command lists. | ||
| 86 | */ | ||
| 87 | void Main(std::stop_token stop_token); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Creates the streams which will receive the processed samples. | ||
| 91 | */ | ||
| 92 | void CreateSinkStreams(); | ||
| 93 | |||
| 94 | /// Core system | ||
| 95 | Core::System& system; | ||
| 96 | /// Memory | ||
| 97 | Core::Memory::Memory& memory; | ||
| 98 | /// The output sink the AudioRenderer will use | ||
| 99 | Sink::Sink& sink; | ||
| 100 | /// The active mailbox | ||
| 101 | Mailbox mailbox; | ||
| 102 | /// Main thread | ||
| 103 | std::jthread main_thread{}; | ||
| 104 | /// The current state | ||
| 105 | std::atomic<bool> running{}; | ||
| 106 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; | ||
| 107 | /// The command lists to process | ||
| 108 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; | ||
| 109 | /// The streams which will receive the processed samples | ||
| 110 | std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; | ||
| 111 | u64 signalled_tick{0}; | ||
| 112 | }; | ||
| 113 | |||
| 114 | } // namespace ADSP::AudioRenderer | ||
| 115 | } // namespace AudioCore | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/command_buffer.h b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h new file mode 100644 index 000000000..3fd1b09dc --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/common/common.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::ADSP::AudioRenderer { | ||
| 10 | |||
| 11 | struct CommandBuffer { | ||
| 12 | // Set by the host | ||
| 13 | CpuAddr buffer{}; | ||
| 14 | u64 size{}; | ||
| 15 | u64 time_limit{}; | ||
| 16 | u64 applet_resource_user_id{}; | ||
| 17 | bool reset_buffer{}; | ||
| 18 | // Set by the DSP | ||
| 19 | u32 remaining_command_count{}; | ||
| 20 | u64 render_time_taken_us{}; | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace AudioCore::ADSP::AudioRenderer | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp new file mode 100644 index 000000000..acbc9100c --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | |||
| 6 | #include "audio_core/adsp/apps/audio_renderer/command_list_processor.h" | ||
| 7 | #include "audio_core/renderer/command/command_list_header.h" | ||
| 8 | #include "audio_core/renderer/command/commands.h" | ||
| 9 | #include "common/settings.h" | ||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/core_timing.h" | ||
| 12 | #include "core/memory.h" | ||
| 13 | |||
| 14 | namespace AudioCore::ADSP::AudioRenderer { | ||
| 15 | |||
| 16 | void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, | ||
| 17 | Sink::SinkStream* stream_) { | ||
| 18 | system = &system_; | ||
| 19 | memory = &system->ApplicationMemory(); | ||
| 20 | stream = stream_; | ||
| 21 | header = reinterpret_cast<Renderer::CommandListHeader*>(buffer); | ||
| 22 | commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader)); | ||
| 23 | commands_buffer_size = size; | ||
| 24 | command_count = header->command_count; | ||
| 25 | sample_count = header->sample_count; | ||
| 26 | target_sample_rate = header->sample_rate; | ||
| 27 | mix_buffers = header->samples_buffer; | ||
| 28 | buffer_count = header->buffer_count; | ||
| 29 | processed_command_count = 0; | ||
| 30 | } | ||
| 31 | |||
| 32 | void CommandListProcessor::SetProcessTimeMax(const u64 time) { | ||
| 33 | max_process_time = time; | ||
| 34 | } | ||
| 35 | |||
| 36 | u32 CommandListProcessor::GetRemainingCommandCount() const { | ||
| 37 | return command_count - processed_command_count; | ||
| 38 | } | ||
| 39 | |||
| 40 | void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) { | ||
| 41 | commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader)); | ||
| 42 | commands_buffer_size = size; | ||
| 43 | } | ||
| 44 | |||
| 45 | Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { | ||
| 46 | return stream; | ||
| 47 | } | ||
| 48 | |||
| 49 | u64 CommandListProcessor::Process(u32 session_id) { | ||
| 50 | const auto start_time_{system->CoreTiming().GetGlobalTimeUs().count()}; | ||
| 51 | const auto command_base{CpuAddr(commands)}; | ||
| 52 | |||
| 53 | if (processed_command_count > 0) { | ||
| 54 | current_processing_time += start_time_ - end_time; | ||
| 55 | } else { | ||
| 56 | start_time = start_time_; | ||
| 57 | current_processing_time = 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | std::string dump{fmt::format("\nSession {}\n", session_id)}; | ||
| 61 | |||
| 62 | for (u32 index = 0; index < command_count; index++) { | ||
| 63 | auto& command{*reinterpret_cast<Renderer::ICommand*>(commands)}; | ||
| 64 | |||
| 65 | if (command.magic != 0xCAFEBABE) { | ||
| 66 | LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", | ||
| 67 | command.magic); | ||
| 68 | return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; | ||
| 69 | } | ||
| 70 | |||
| 71 | auto current_offset{CpuAddr(commands) - command_base}; | ||
| 72 | |||
| 73 | if (current_offset + command.size > commands_buffer_size) { | ||
| 74 | LOG_ERROR(Service_Audio, | ||
| 75 | "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", | ||
| 76 | commands_buffer_size, | ||
| 77 | CpuAddr(commands) + command.size - sizeof(Renderer::CommandListHeader)); | ||
| 78 | return system->CoreTiming().GetGlobalTimeUs().count() - start_time_; | ||
| 79 | } | ||
| 80 | |||
| 81 | if (Settings::values.dump_audio_commands) { | ||
| 82 | command.Dump(*this, dump); | ||
| 83 | } | ||
| 84 | |||
| 85 | if (!command.Verify(*this)) { | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (command.enabled) { | ||
| 90 | command.Process(*this); | ||
| 91 | } else { | ||
| 92 | dump += fmt::format("\tDisabled!\n"); | ||
| 93 | } | ||
| 94 | |||
| 95 | processed_command_count++; | ||
| 96 | commands += command.size; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (Settings::values.dump_audio_commands && dump != last_dump) { | ||
| 100 | LOG_WARNING(Service_Audio, "{}", dump); | ||
| 101 | last_dump = dump; | ||
| 102 | } | ||
| 103 | |||
| 104 | end_time = system->CoreTiming().GetGlobalTimeUs().count(); | ||
| 105 | return end_time - start_time_; | ||
| 106 | } | ||
| 107 | |||
| 108 | } // namespace AudioCore::ADSP::AudioRenderer | ||
diff --git a/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h new file mode 100644 index 000000000..9d6fe1851 --- /dev/null +++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/command/command_list_header.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | namespace Memory { | ||
| 14 | class Memory; | ||
| 15 | } | ||
| 16 | class System; | ||
| 17 | } // namespace Core | ||
| 18 | |||
| 19 | namespace AudioCore { | ||
| 20 | namespace Sink { | ||
| 21 | class SinkStream; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Renderer { | ||
| 25 | struct CommandListHeader; | ||
| 26 | } | ||
| 27 | |||
| 28 | namespace ADSP::AudioRenderer { | ||
| 29 | |||
| 30 | /** | ||
| 31 | * A processor for command lists given to the AudioRenderer. | ||
| 32 | */ | ||
| 33 | class CommandListProcessor { | ||
| 34 | public: | ||
| 35 | /** | ||
| 36 | * Initialize the processor. | ||
| 37 | * | ||
| 38 | * @param system - The core system. | ||
| 39 | * @param buffer - The command buffer to process. | ||
| 40 | * @param size - The size of the buffer. | ||
| 41 | * @param stream - The stream to be used for sending the samples. | ||
| 42 | */ | ||
| 43 | void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream); | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Set the maximum processing time for this command list. | ||
| 47 | * | ||
| 48 | * @param time - The maximum process time. | ||
| 49 | */ | ||
| 50 | void SetProcessTimeMax(u64 time); | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Get the remaining command count for this list. | ||
| 54 | * | ||
| 55 | * @return The remaining command count. | ||
| 56 | */ | ||
| 57 | u32 GetRemainingCommandCount() const; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Set the command buffer. | ||
| 61 | * | ||
| 62 | * @param buffer - The buffer to use. | ||
| 63 | * @param size - The size of the buffer. | ||
| 64 | */ | ||
| 65 | void SetBuffer(CpuAddr buffer, u64 size); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Get the stream for this command list. | ||
| 69 | * | ||
| 70 | * @return The stream associated with this command list. | ||
| 71 | */ | ||
| 72 | Sink::SinkStream* GetOutputSinkStream() const; | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Process the command list. | ||
| 76 | * | ||
| 77 | * @param session_id - Session ID for the commands being processed. | ||
| 78 | * | ||
| 79 | * @return The time taken to process. | ||
| 80 | */ | ||
| 81 | u64 Process(u32 session_id); | ||
| 82 | |||
| 83 | /// Core system | ||
| 84 | Core::System* system{}; | ||
| 85 | /// Core memory | ||
| 86 | Core::Memory::Memory* memory{}; | ||
| 87 | /// Stream for the processed samples | ||
| 88 | Sink::SinkStream* stream{}; | ||
| 89 | /// Header info for this command list | ||
| 90 | Renderer::CommandListHeader* header{}; | ||
| 91 | /// The command buffer | ||
| 92 | u8* commands{}; | ||
| 93 | /// The command buffer size | ||
| 94 | u64 commands_buffer_size{}; | ||
| 95 | /// The maximum processing time allotted | ||
| 96 | u64 max_process_time{}; | ||
| 97 | /// The number of commands in the buffer | ||
| 98 | u32 command_count{}; | ||
| 99 | /// The target sample count for output | ||
| 100 | u32 sample_count{}; | ||
| 101 | /// The target sample rate for output | ||
| 102 | u32 target_sample_rate{}; | ||
| 103 | /// The mixing buffers used by the commands | ||
| 104 | std::span<s32> mix_buffers{}; | ||
| 105 | /// The number of mix buffers | ||
| 106 | u32 buffer_count{}; | ||
| 107 | /// The number of processed commands so far | ||
| 108 | u32 processed_command_count{}; | ||
| 109 | /// The processing start time of this list | ||
| 110 | u64 start_time{}; | ||
| 111 | /// The current processing time for this list | ||
| 112 | u64 current_processing_time{}; | ||
| 113 | /// The end processing time for this list | ||
| 114 | u64 end_time{}; | ||
| 115 | /// Last command list string generated, used for dumping audio commands to console | ||
| 116 | std::string last_dump{}; | ||
| 117 | }; | ||
| 118 | |||
| 119 | } // namespace ADSP::AudioRenderer | ||
| 120 | } // namespace AudioCore | ||
diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h new file mode 100644 index 000000000..c31b73717 --- /dev/null +++ b/src/audio_core/adsp/mailbox.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/bounded_threadsafe_queue.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::ADSP { | ||
| 10 | |||
| 11 | enum class AppMailboxId : u32 { | ||
| 12 | Invalid = 0, | ||
| 13 | AudioRenderer = 50, | ||
| 14 | AudioRendererMemoryMapUnmap = 51, | ||
| 15 | }; | ||
| 16 | |||
| 17 | enum class Direction : u32 { | ||
| 18 | Host, | ||
| 19 | DSP, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct MailboxMessage { | ||
| 23 | u32 msg; | ||
| 24 | std::span<u8> data; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class Mailbox { | ||
| 28 | public: | ||
| 29 | void Initialize(AppMailboxId id_) { | ||
| 30 | Reset(); | ||
| 31 | id = id_; | ||
| 32 | } | ||
| 33 | |||
| 34 | AppMailboxId Id() const noexcept { | ||
| 35 | return id; | ||
| 36 | } | ||
| 37 | |||
| 38 | void Send(Direction dir, MailboxMessage&& message) { | ||
| 39 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | ||
| 40 | queue.EmplaceWait(std::move(message)); | ||
| 41 | } | ||
| 42 | |||
| 43 | MailboxMessage Receive(Direction dir, bool block = true) { | ||
| 44 | auto& queue = dir == Direction::Host ? host_queue : adsp_queue; | ||
| 45 | MailboxMessage t; | ||
| 46 | if (block) { | ||
| 47 | queue.PopWait(t); | ||
| 48 | } else { | ||
| 49 | queue.TryPop(t); | ||
| 50 | } | ||
| 51 | return t; | ||
| 52 | } | ||
| 53 | |||
| 54 | void Reset() { | ||
| 55 | id = AppMailboxId::Invalid; | ||
| 56 | MailboxMessage t; | ||
| 57 | while (host_queue.TryPop(t)) { | ||
| 58 | } | ||
| 59 | while (adsp_queue.TryPop(t)) { | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | private: | ||
| 64 | AppMailboxId id{0}; | ||
| 65 | Common::SPSCQueue<MailboxMessage> host_queue; | ||
| 66 | Common::SPSCQueue<MailboxMessage> adsp_queue; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace AudioCore::ADSP | ||