diff options
| author | 2022-07-16 23:48:45 +0100 | |
|---|---|---|
| committer | 2022-07-22 01:11:32 +0100 | |
| commit | 458da8a94877677f086f06cdeecf959ec4283a33 (patch) | |
| tree | 583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/adsp | |
| parent | Merge pull request #8598 from Link4565/recv-dontwait (diff) | |
| download | yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip | |
Project Andio
Diffstat (limited to 'src/audio_core/renderer/adsp')
| -rw-r--r-- | src/audio_core/renderer/adsp/adsp.cpp | 118 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/adsp.h | 173 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.cpp | 226 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.h | 203 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/command_buffer.h | 21 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/command_list_processor.cpp | 109 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/command_list_processor.h | 118 |
7 files changed, 968 insertions, 0 deletions
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp new file mode 100644 index 000000000..e05a22d86 --- /dev/null +++ b/src/audio_core/renderer/adsp/adsp.cpp | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/adsp/adsp.h" | ||
| 5 | #include "audio_core/renderer/adsp/command_buffer.h" | ||
| 6 | #include "audio_core/sink/sink.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/core_timing_util.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | |||
| 13 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 14 | |||
| 15 | ADSP::ADSP(Core::System& system_, Sink::Sink& sink_) | ||
| 16 | : system{system_}, memory{system.Memory()}, sink{sink_} {} | ||
| 17 | |||
| 18 | ADSP::~ADSP() { | ||
| 19 | ClearCommandBuffers(); | ||
| 20 | } | ||
| 21 | |||
| 22 | State ADSP::GetState() const { | ||
| 23 | if (running) { | ||
| 24 | return State::Started; | ||
| 25 | } | ||
| 26 | return State::Stopped; | ||
| 27 | } | ||
| 28 | |||
| 29 | AudioRenderer_Mailbox* ADSP::GetRenderMailbox() { | ||
| 30 | return &render_mailbox; | ||
| 31 | } | ||
| 32 | |||
| 33 | void ADSP::ClearRemainCount(const u32 session_id) { | ||
| 34 | render_mailbox.ClearRemainCount(session_id); | ||
| 35 | } | ||
| 36 | |||
| 37 | u64 ADSP::GetSignalledTick() const { | ||
| 38 | return render_mailbox.GetSignalledTick(); | ||
| 39 | } | ||
| 40 | |||
| 41 | u64 ADSP::GetTimeTaken() const { | ||
| 42 | return render_mailbox.GetRenderTimeTaken(); | ||
| 43 | } | ||
| 44 | |||
| 45 | u64 ADSP::GetRenderTimeTaken(const u32 session_id) { | ||
| 46 | return render_mailbox.GetCommandBuffer(session_id).render_time_taken; | ||
| 47 | } | ||
| 48 | |||
| 49 | u32 ADSP::GetRemainCommandCount(const u32 session_id) const { | ||
| 50 | return render_mailbox.GetRemainCommandCount(session_id); | ||
| 51 | } | ||
| 52 | |||
| 53 | void ADSP::SendCommandBuffer(const u32 session_id, CommandBuffer& command_buffer) { | ||
| 54 | render_mailbox.SetCommandBuffer(session_id, command_buffer); | ||
| 55 | } | ||
| 56 | |||
| 57 | u64 ADSP::GetRenderingStartTick(const u32 session_id) { | ||
| 58 | return render_mailbox.GetSignalledTick() + | ||
| 59 | render_mailbox.GetCommandBuffer(session_id).render_time_taken; | ||
| 60 | } | ||
| 61 | |||
| 62 | bool ADSP::Start() { | ||
| 63 | if (running) { | ||
| 64 | return running; | ||
| 65 | } | ||
| 66 | |||
| 67 | running = true; | ||
| 68 | systems_active++; | ||
| 69 | audio_renderer = std::make_unique<AudioRenderer>(system); | ||
| 70 | audio_renderer->Start(&render_mailbox); | ||
| 71 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_InitializeOK); | ||
| 72 | if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { | ||
| 73 | LOG_ERROR( | ||
| 74 | Service_Audio, | ||
| 75 | "Host Audio Renderer -- Failed to receive initialize message response from ADSP!"); | ||
| 76 | } | ||
| 77 | return running; | ||
| 78 | } | ||
| 79 | |||
| 80 | void ADSP::Stop() { | ||
| 81 | systems_active--; | ||
| 82 | if (running && systems_active == 0) { | ||
| 83 | { | ||
| 84 | std::scoped_lock l{mailbox_lock}; | ||
| 85 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Shutdown); | ||
| 86 | if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_Shutdown) { | ||
| 87 | LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " | ||
| 88 | "message response from ADSP!"); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | audio_renderer->Stop(); | ||
| 92 | running = false; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | void ADSP::Signal() { | ||
| 97 | const auto signalled_tick{system.CoreTiming().GetClockTicks()}; | ||
| 98 | render_mailbox.SetSignalledTick(signalled_tick); | ||
| 99 | render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Render); | ||
| 100 | } | ||
| 101 | |||
| 102 | void ADSP::Wait() { | ||
| 103 | std::scoped_lock l{mailbox_lock}; | ||
| 104 | auto response{render_mailbox.HostWaitMessage()}; | ||
| 105 | if (response != RenderMessage::AudioRenderer_RenderResponse) { | ||
| 106 | LOG_ERROR(Service_Audio, "Invalid ADSP response message, expected 0x{:02X}, got 0x{:02X}", | ||
| 107 | static_cast<u32>(RenderMessage::AudioRenderer_RenderResponse), | ||
| 108 | static_cast<u32>(response)); | ||
| 109 | } | ||
| 110 | |||
| 111 | ClearCommandBuffers(); | ||
| 112 | } | ||
| 113 | |||
| 114 | void ADSP::ClearCommandBuffers() { | ||
| 115 | render_mailbox.ClearCommandBuffers(); | ||
| 116 | } | ||
| 117 | |||
| 118 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h new file mode 100644 index 000000000..4dfcef4a5 --- /dev/null +++ b/src/audio_core/renderer/adsp/adsp.h | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "audio_core/renderer/adsp/audio_renderer.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 Sink; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace AudioRenderer::ADSP { | ||
| 25 | struct CommandBuffer; | ||
| 26 | |||
| 27 | enum class State { | ||
| 28 | Started, | ||
| 29 | Stopped, | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Represents the ADSP embedded within the audio sysmodule. | ||
| 34 | * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot. | ||
| 35 | * | ||
| 36 | * The kernel will run apps you program for it, Nintendo have the following: | ||
| 37 | * | ||
| 38 | * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all | ||
| 39 | * audio samples end up, and we skip it entirely, since we have very different backends and | ||
| 40 | * mixing is implicitly handled by the OS (but also due to lack of research/simplicity). | ||
| 41 | * | ||
| 42 | * AudioRenderer - Receives command lists generated by the audio render | ||
| 43 | * system, processes them, and sends the samples to Gmix. | ||
| 44 | * | ||
| 45 | * OpusDecoder - Contains libopus, and controls processing Opus audio and sends it to Gmix. | ||
| 46 | * Not much research done here, TODO if needed. | ||
| 47 | * | ||
| 48 | * We only implement the AudioRenderer for now. | ||
| 49 | * | ||
| 50 | * Communication for the apps is done through mailboxes, and some shared memory. | ||
| 51 | */ | ||
| 52 | class ADSP { | ||
| 53 | public: | ||
| 54 | explicit ADSP(Core::System& system, Sink::Sink& sink); | ||
| 55 | ~ADSP(); | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Start the ADSP. | ||
| 59 | * | ||
| 60 | * @return True if started or already running, otherwise false. | ||
| 61 | */ | ||
| 62 | bool Start(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Stop the ADSP. | ||
| 66 | * | ||
| 67 | * @return True if started or already running, otherwise false. | ||
| 68 | */ | ||
| 69 | void Stop(); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Get the ADSP's state. | ||
| 73 | * | ||
| 74 | * @return Started or Stopped. | ||
| 75 | */ | ||
| 76 | State GetState() const; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Get the AudioRenderer mailbox to communicate with it. | ||
| 80 | * | ||
| 81 | * @return The AudioRenderer mailbox. | ||
| 82 | */ | ||
| 83 | AudioRenderer_Mailbox* GetRenderMailbox(); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Get the tick the ADSP was signalled. | ||
| 87 | * | ||
| 88 | * @return The tick the ADSP was signalled. | ||
| 89 | */ | ||
| 90 | u64 GetSignalledTick() const; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Get the total time it took for the ADSP to run the last command lists (both command lists). | ||
| 94 | * | ||
| 95 | * @return The tick the ADSP was signalled. | ||
| 96 | */ | ||
| 97 | u64 GetTimeTaken() const; | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Get the last time a given command list took to run. | ||
| 101 | * | ||
| 102 | * @param session_id - The session id to check (0 or 1). | ||
| 103 | * @return The time it took. | ||
| 104 | */ | ||
| 105 | u64 GetRenderTimeTaken(u32 session_id); | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Clear the remaining command count for a given session. | ||
| 109 | * | ||
| 110 | * @param session_id - The session id to check (0 or 1). | ||
| 111 | */ | ||
| 112 | void ClearRemainCount(u32 session_id); | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Get the remaining number of commands left to process for a command list. | ||
| 116 | * | ||
| 117 | * @param session_id - The session id to check (0 or 1). | ||
| 118 | * @return The number of commands remaining. | ||
| 119 | */ | ||
| 120 | u32 GetRemainCommandCount(u32 session_id) const; | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Get the last tick a command list started processing. | ||
| 124 | * | ||
| 125 | * @param session_id - The session id to check (0 or 1). | ||
| 126 | * @return The last tick the given command list started. | ||
| 127 | */ | ||
| 128 | u64 GetRenderingStartTick(u32 session_id); | ||
| 129 | |||
| 130 | /** | ||
| 131 | * Set a command buffer to be processed. | ||
| 132 | * | ||
| 133 | * @param session_id - The session id to check (0 or 1). | ||
| 134 | * @param command_buffer - The command buffer to process. | ||
| 135 | */ | ||
| 136 | void SendCommandBuffer(u32 session_id, CommandBuffer& command_buffer); | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Clear the command buffers (does not clear the time taken or the remaining command count) | ||
| 140 | */ | ||
| 141 | void ClearCommandBuffers(); | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Signal the AudioRenderer to begin processing. | ||
| 145 | */ | ||
| 146 | void Signal(); | ||
| 147 | |||
| 148 | /** | ||
| 149 | * Wait for the AudioRenderer to finish processing. | ||
| 150 | */ | ||
| 151 | void Wait(); | ||
| 152 | |||
| 153 | private: | ||
| 154 | /// Core system | ||
| 155 | Core::System& system; | ||
| 156 | /// Core memory | ||
| 157 | Core::Memory::Memory& memory; | ||
| 158 | /// Number of systems active, used to prevent accidental shutdowns | ||
| 159 | u8 systems_active{0}; | ||
| 160 | /// ADSP running state | ||
| 161 | std::atomic<bool> running{false}; | ||
| 162 | /// Output sink used by the ADSP | ||
| 163 | Sink::Sink& sink; | ||
| 164 | /// AudioRenderer app | ||
| 165 | std::unique_ptr<AudioRenderer> audio_renderer{}; | ||
| 166 | /// Communication for the AudioRenderer | ||
| 167 | AudioRenderer_Mailbox render_mailbox{}; | ||
| 168 | /// Mailbox lock ffor the render mailbox | ||
| 169 | std::mutex mailbox_lock; | ||
| 170 | }; | ||
| 171 | |||
| 172 | } // namespace AudioRenderer::ADSP | ||
| 173 | } // namespace AudioCore | ||
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp new file mode 100644 index 000000000..3967ccfe6 --- /dev/null +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | |||
| 7 | #include "audio_core/audio_core.h" | ||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/adsp/audio_renderer.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 | #include "core/core_timing_util.h" | ||
| 17 | |||
| 18 | MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); | ||
| 19 | |||
| 20 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 21 | |||
| 22 | void AudioRenderer_Mailbox::HostSendMessage(RenderMessage message_) { | ||
| 23 | adsp_messages.enqueue(message_); | ||
| 24 | adsp_event.Set(); | ||
| 25 | } | ||
| 26 | |||
| 27 | RenderMessage AudioRenderer_Mailbox::HostWaitMessage() { | ||
| 28 | host_event.Wait(); | ||
| 29 | RenderMessage msg{RenderMessage::Invalid}; | ||
| 30 | if (!host_messages.try_dequeue(msg)) { | ||
| 31 | LOG_ERROR(Service_Audio, "Failed to dequeue host message!"); | ||
| 32 | } | ||
| 33 | return msg; | ||
| 34 | } | ||
| 35 | |||
| 36 | void AudioRenderer_Mailbox::ADSPSendMessage(const RenderMessage message_) { | ||
| 37 | host_messages.enqueue(message_); | ||
| 38 | host_event.Set(); | ||
| 39 | } | ||
| 40 | |||
| 41 | RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() { | ||
| 42 | adsp_event.Wait(); | ||
| 43 | RenderMessage msg{RenderMessage::Invalid}; | ||
| 44 | if (!adsp_messages.try_dequeue(msg)) { | ||
| 45 | LOG_ERROR(Service_Audio, "Failed to dequeue ADSP message!"); | ||
| 46 | } | ||
| 47 | return msg; | ||
| 48 | } | ||
| 49 | |||
| 50 | CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) { | ||
| 51 | return command_buffers[session_id]; | ||
| 52 | } | ||
| 53 | |||
| 54 | void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, CommandBuffer& buffer) { | ||
| 55 | command_buffers[session_id] = buffer; | ||
| 56 | } | ||
| 57 | |||
| 58 | u64 AudioRenderer_Mailbox::GetRenderTimeTaken() const { | ||
| 59 | return command_buffers[0].render_time_taken + command_buffers[1].render_time_taken; | ||
| 60 | } | ||
| 61 | |||
| 62 | u64 AudioRenderer_Mailbox::GetSignalledTick() const { | ||
| 63 | return signalled_tick; | ||
| 64 | } | ||
| 65 | |||
| 66 | void AudioRenderer_Mailbox::SetSignalledTick(const u64 tick) { | ||
| 67 | signalled_tick = tick; | ||
| 68 | } | ||
| 69 | |||
| 70 | void AudioRenderer_Mailbox::ClearRemainCount(const u32 session_id) { | ||
| 71 | command_buffers[session_id].remaining_command_count = 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | u32 AudioRenderer_Mailbox::GetRemainCommandCount(const u32 session_id) const { | ||
| 75 | return command_buffers[session_id].remaining_command_count; | ||
| 76 | } | ||
| 77 | |||
| 78 | void AudioRenderer_Mailbox::ClearCommandBuffers() { | ||
| 79 | command_buffers[0].buffer = 0; | ||
| 80 | command_buffers[0].size = 0; | ||
| 81 | command_buffers[0].reset_buffers = false; | ||
| 82 | command_buffers[1].buffer = 0; | ||
| 83 | command_buffers[1].size = 0; | ||
| 84 | command_buffers[1].reset_buffers = false; | ||
| 85 | } | ||
| 86 | |||
| 87 | AudioRenderer::AudioRenderer(Core::System& system_) | ||
| 88 | : system{system_}, sink{system.AudioCore().GetOutputSink()} { | ||
| 89 | CreateSinkStreams(); | ||
| 90 | } | ||
| 91 | |||
| 92 | AudioRenderer::~AudioRenderer() { | ||
| 93 | Stop(); | ||
| 94 | for (auto& stream : streams) { | ||
| 95 | if (stream) { | ||
| 96 | sink.CloseStream(stream); | ||
| 97 | } | ||
| 98 | stream = nullptr; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) { | ||
| 103 | if (running) { | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | |||
| 107 | mailbox = mailbox_; | ||
| 108 | thread = std::thread(&AudioRenderer::ThreadFunc, this); | ||
| 109 | for (auto& stream : streams) { | ||
| 110 | stream->Start(); | ||
| 111 | } | ||
| 112 | running = true; | ||
| 113 | } | ||
| 114 | |||
| 115 | void AudioRenderer::Stop() { | ||
| 116 | if (!running) { | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | |||
| 120 | for (auto& stream : streams) { | ||
| 121 | stream->Stop(); | ||
| 122 | } | ||
| 123 | thread.join(); | ||
| 124 | running = false; | ||
| 125 | } | ||
| 126 | |||
| 127 | void AudioRenderer::CreateSinkStreams() { | ||
| 128 | u32 channels{sink.GetDeviceChannels()}; | ||
| 129 | for (u32 i = 0; i < MaxRendererSessions; i++) { | ||
| 130 | std::string name{fmt::format("ADSP_RenderStream-{}", i)}; | ||
| 131 | streams[i] = | ||
| 132 | sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render); | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | void AudioRenderer::ThreadFunc() { | ||
| 137 | constexpr char name[]{"yuzu:AudioRenderer"}; | ||
| 138 | MicroProfileOnThreadCreate(name); | ||
| 139 | Common::SetCurrentThreadName(name); | ||
| 140 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); | ||
| 141 | if (mailbox->ADSPWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) { | ||
| 142 | LOG_ERROR(Service_Audio, | ||
| 143 | "ADSP Audio Renderer -- Failed to receive initialize message from host!"); | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | |||
| 147 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK); | ||
| 148 | |||
| 149 | constexpr u64 max_process_time{2'304'000ULL}; | ||
| 150 | |||
| 151 | while (true) { | ||
| 152 | auto message{mailbox->ADSPWaitMessage()}; | ||
| 153 | switch (message) { | ||
| 154 | case RenderMessage::AudioRenderer_Shutdown: | ||
| 155 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_Shutdown); | ||
| 156 | return; | ||
| 157 | |||
| 158 | case RenderMessage::AudioRenderer_Render: { | ||
| 159 | std::array<bool, MaxRendererSessions> buffers_reset{}; | ||
| 160 | std::array<u64, MaxRendererSessions> render_times_taken{}; | ||
| 161 | const auto start_time{system.CoreTiming().GetClockTicks()}; | ||
| 162 | |||
| 163 | for (u32 index = 0; index < 2; index++) { | ||
| 164 | auto& command_buffer{mailbox->GetCommandBuffer(index)}; | ||
| 165 | auto& command_list_processor{command_list_processors[index]}; | ||
| 166 | |||
| 167 | // Check this buffer is valid, as it may not be used. | ||
| 168 | if (command_buffer.buffer != 0) { | ||
| 169 | // If there are no remaining commands (from the previous list), | ||
| 170 | // this is a new command list, initalize it. | ||
| 171 | if (command_buffer.remaining_command_count == 0) { | ||
| 172 | command_list_processor.Initialize(system, command_buffer.buffer, | ||
| 173 | command_buffer.size, streams[index]); | ||
| 174 | } | ||
| 175 | |||
| 176 | if (command_buffer.reset_buffers && !buffers_reset[index]) { | ||
| 177 | streams[index]->ClearQueue(); | ||
| 178 | buffers_reset[index] = true; | ||
| 179 | } | ||
| 180 | |||
| 181 | u64 max_time{max_process_time}; | ||
| 182 | if (index == 1 && command_buffer.applet_resource_user_id == | ||
| 183 | mailbox->GetCommandBuffer(0).applet_resource_user_id) { | ||
| 184 | max_time = max_process_time - | ||
| 185 | Core::Timing::CyclesToNs(render_times_taken[0]).count(); | ||
| 186 | if (render_times_taken[0] > max_process_time) { | ||
| 187 | max_time = 0; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | max_time = std::min(command_buffer.time_limit, max_time); | ||
| 192 | command_list_processor.SetProcessTimeMax(max_time); | ||
| 193 | |||
| 194 | // Process the command list | ||
| 195 | { | ||
| 196 | MICROPROFILE_SCOPE(Audio_Renderer); | ||
| 197 | render_times_taken[index] = | ||
| 198 | command_list_processor.Process(index) - start_time; | ||
| 199 | } | ||
| 200 | |||
| 201 | if (index == 0) { | ||
| 202 | auto stream{command_list_processor.GetOutputSinkStream()}; | ||
| 203 | system.AudioCore().SetStreamQueue(stream->GetQueueSize()); | ||
| 204 | } | ||
| 205 | |||
| 206 | const auto end_time{system.CoreTiming().GetClockTicks()}; | ||
| 207 | |||
| 208 | command_buffer.remaining_command_count = | ||
| 209 | command_list_processor.GetRemainingCommandCount(); | ||
| 210 | command_buffer.render_time_taken = end_time - start_time; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse); | ||
| 215 | } break; | ||
| 216 | |||
| 217 | default: | ||
| 218 | LOG_WARNING(Service_Audio, | ||
| 219 | "ADSP AudioRenderer received an invalid message, msg={:02X}!", | ||
| 220 | static_cast<u32>(message)); | ||
| 221 | break; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h new file mode 100644 index 000000000..b6ced9d2b --- /dev/null +++ b/src/audio_core/renderer/adsp/audio_renderer.h | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 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/renderer/adsp/command_buffer.h" | ||
| 11 | #include "audio_core/renderer/adsp/command_list_processor.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/reader_writer_queue.h" | ||
| 14 | #include "common/thread.h" | ||
| 15 | |||
| 16 | namespace Core { | ||
| 17 | namespace Timing { | ||
| 18 | struct EventType; | ||
| 19 | } | ||
| 20 | class System; | ||
| 21 | } // namespace Core | ||
| 22 | |||
| 23 | namespace AudioCore { | ||
| 24 | namespace Sink { | ||
| 25 | class Sink; | ||
| 26 | } | ||
| 27 | |||
| 28 | namespace AudioRenderer::ADSP { | ||
| 29 | |||
| 30 | enum class RenderMessage { | ||
| 31 | /* 0x00 */ Invalid, | ||
| 32 | /* 0x01 */ AudioRenderer_MapUnmap_Map, | ||
| 33 | /* 0x02 */ AudioRenderer_MapUnmap_MapResponse, | ||
| 34 | /* 0x03 */ AudioRenderer_MapUnmap_Unmap, | ||
| 35 | /* 0x04 */ AudioRenderer_MapUnmap_UnmapResponse, | ||
| 36 | /* 0x05 */ AudioRenderer_MapUnmap_InvalidateCache, | ||
| 37 | /* 0x06 */ AudioRenderer_MapUnmap_InvalidateCacheResponse, | ||
| 38 | /* 0x07 */ AudioRenderer_MapUnmap_Shutdown, | ||
| 39 | /* 0x08 */ AudioRenderer_MapUnmap_ShutdownResponse, | ||
| 40 | /* 0x16 */ AudioRenderer_InitializeOK = 0x16, | ||
| 41 | /* 0x20 */ AudioRenderer_RenderResponse = 0x20, | ||
| 42 | /* 0x2A */ AudioRenderer_Render = 0x2A, | ||
| 43 | /* 0x34 */ AudioRenderer_Shutdown = 0x34, | ||
| 44 | }; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * A mailbox for the AudioRenderer, allowing communication between the host and the AudioRenderer | ||
| 48 | * running on the ADSP. | ||
| 49 | */ | ||
| 50 | class AudioRenderer_Mailbox { | ||
| 51 | public: | ||
| 52 | /** | ||
| 53 | * Send a message from the host to the AudioRenderer. | ||
| 54 | * | ||
| 55 | * @param message_ - The message to send to the AudioRenderer. | ||
| 56 | */ | ||
| 57 | void HostSendMessage(RenderMessage message); | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Host wait for a message from the AudioRenderer. | ||
| 61 | * | ||
| 62 | * @return The message returned from the AudioRenderer. | ||
| 63 | */ | ||
| 64 | RenderMessage HostWaitMessage(); | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Send a message from the AudioRenderer to the host. | ||
| 68 | * | ||
| 69 | * @param message_ - The message to send to the host. | ||
| 70 | */ | ||
| 71 | void ADSPSendMessage(RenderMessage message); | ||
| 72 | |||
| 73 | /** | ||
| 74 | * AudioRenderer wait for a message from the host. | ||
| 75 | * | ||
| 76 | * @return The message returned from the AudioRenderer. | ||
| 77 | */ | ||
| 78 | RenderMessage ADSPWaitMessage(); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Get the command buffer with the given session id (0 or 1). | ||
| 82 | * | ||
| 83 | * @param session_id - The session id to get (0 or 1). | ||
| 84 | * @return The command buffer. | ||
| 85 | */ | ||
| 86 | CommandBuffer& GetCommandBuffer(s32 session_id); | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Set the command buffer with the given session id (0 or 1). | ||
| 90 | * | ||
| 91 | * @param session_id - The session id to get (0 or 1). | ||
| 92 | * @param buffer - The command buffer to set. | ||
| 93 | */ | ||
| 94 | void SetCommandBuffer(u32 session_id, CommandBuffer& buffer); | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Get the total render time taken for the last command lists sent. | ||
| 98 | * | ||
| 99 | * @return Total render time taken for the last command lists. | ||
| 100 | */ | ||
| 101 | u64 GetRenderTimeTaken() const; | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Get the tick the AudioRenderer was signalled. | ||
| 105 | * | ||
| 106 | * @return The tick the AudioRenderer was signalled. | ||
| 107 | */ | ||
| 108 | u64 GetSignalledTick() const; | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Set the tick the AudioRenderer was signalled. | ||
| 112 | * | ||
| 113 | * @param tick - The tick the AudioRenderer was signalled. | ||
| 114 | */ | ||
| 115 | void SetSignalledTick(u64 tick); | ||
| 116 | |||
| 117 | /** | ||
| 118 | * Clear the remaining command count. | ||
| 119 | * | ||
| 120 | * @param session_id - Index for which command list to clear (0 or 1). | ||
| 121 | */ | ||
| 122 | void ClearRemainCount(u32 session_id); | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Get the remaining command count for a given command list. | ||
| 126 | * | ||
| 127 | * @param session_id - Index for which command list to clear (0 or 1). | ||
| 128 | * @return The remaining command count. | ||
| 129 | */ | ||
| 130 | u32 GetRemainCommandCount(u32 session_id) const; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Clear the command buffers (does not clear the time taken or the remaining command count). | ||
| 134 | */ | ||
| 135 | void ClearCommandBuffers(); | ||
| 136 | |||
| 137 | private: | ||
| 138 | /// Host signalling event | ||
| 139 | Common::Event host_event{}; | ||
| 140 | /// AudioRenderer signalling event | ||
| 141 | Common::Event adsp_event{}; | ||
| 142 | /// Host message queue | ||
| 143 | |||
| 144 | Common::ReaderWriterQueue<RenderMessage> host_messages{}; | ||
| 145 | /// AudioRenderer message queue | ||
| 146 | |||
| 147 | Common::ReaderWriterQueue<RenderMessage> adsp_messages{}; | ||
| 148 | /// Command buffers | ||
| 149 | |||
| 150 | std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; | ||
| 151 | /// Tick the AudioRnederer was signalled | ||
| 152 | u64 signalled_tick{}; | ||
| 153 | }; | ||
| 154 | |||
| 155 | /** | ||
| 156 | * The AudioRenderer application running on the ADSP. | ||
| 157 | */ | ||
| 158 | class AudioRenderer { | ||
| 159 | public: | ||
| 160 | explicit AudioRenderer(Core::System& system); | ||
| 161 | ~AudioRenderer(); | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Start the AudioRenderer. | ||
| 165 | * | ||
| 166 | * @param The mailbox to use for this session. | ||
| 167 | */ | ||
| 168 | void Start(AudioRenderer_Mailbox* mailbox); | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Stop the AudioRenderer. | ||
| 172 | */ | ||
| 173 | void Stop(); | ||
| 174 | |||
| 175 | private: | ||
| 176 | /** | ||
| 177 | * Main AudioRenderer thread, responsible for processing the command lists. | ||
| 178 | */ | ||
| 179 | void ThreadFunc(); | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Creates the streams which will receive the processed samples. | ||
| 183 | */ | ||
| 184 | void CreateSinkStreams(); | ||
| 185 | |||
| 186 | /// Core system | ||
| 187 | Core::System& system; | ||
| 188 | /// Main thread | ||
| 189 | std::thread thread{}; | ||
| 190 | /// The current state | ||
| 191 | std::atomic<bool> running{}; | ||
| 192 | /// The active mailbox | ||
| 193 | AudioRenderer_Mailbox* mailbox{}; | ||
| 194 | /// The command lists to process | ||
| 195 | std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; | ||
| 196 | /// The output sink the AudioRenderer will use | ||
| 197 | Sink::Sink& sink; | ||
| 198 | /// The streams which will receive the processed samples | ||
| 199 | std::array<Sink::SinkStream*, MaxRendererSessions> streams; | ||
| 200 | }; | ||
| 201 | |||
| 202 | } // namespace AudioRenderer::ADSP | ||
| 203 | } // namespace AudioCore | ||
diff --git a/src/audio_core/renderer/adsp/command_buffer.h b/src/audio_core/renderer/adsp/command_buffer.h new file mode 100644 index 000000000..880b279d8 --- /dev/null +++ b/src/audio_core/renderer/adsp/command_buffer.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 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::AudioRenderer::ADSP { | ||
| 10 | |||
| 11 | struct CommandBuffer { | ||
| 12 | CpuAddr buffer; | ||
| 13 | u64 size; | ||
| 14 | u64 time_limit; | ||
| 15 | u32 remaining_command_count; | ||
| 16 | bool reset_buffers; | ||
| 17 | u64 applet_resource_user_id; | ||
| 18 | u64 render_time_taken; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/renderer/adsp/command_list_processor.cpp new file mode 100644 index 000000000..e3bf2d7ec --- /dev/null +++ b/src/audio_core/renderer/adsp/command_list_processor.cpp | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | |||
| 6 | #include "audio_core/renderer/adsp/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/core_timing_util.h" | ||
| 13 | #include "core/memory.h" | ||
| 14 | |||
| 15 | namespace AudioCore::AudioRenderer::ADSP { | ||
| 16 | |||
| 17 | void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, | ||
| 18 | Sink::SinkStream* stream_) { | ||
| 19 | system = &system_; | ||
| 20 | memory = &system->Memory(); | ||
| 21 | stream = stream_; | ||
| 22 | header = reinterpret_cast<CommandListHeader*>(buffer); | ||
| 23 | commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); | ||
| 24 | commands_buffer_size = size; | ||
| 25 | command_count = header->command_count; | ||
| 26 | sample_count = header->sample_count; | ||
| 27 | target_sample_rate = header->sample_rate; | ||
| 28 | mix_buffers = header->samples_buffer; | ||
| 29 | buffer_count = header->buffer_count; | ||
| 30 | processed_command_count = 0; | ||
| 31 | } | ||
| 32 | |||
| 33 | void CommandListProcessor::SetProcessTimeMax(const u64 time) { | ||
| 34 | max_process_time = time; | ||
| 35 | } | ||
| 36 | |||
| 37 | u32 CommandListProcessor::GetRemainingCommandCount() const { | ||
| 38 | return command_count - processed_command_count; | ||
| 39 | } | ||
| 40 | |||
| 41 | void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) { | ||
| 42 | commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); | ||
| 43 | commands_buffer_size = size; | ||
| 44 | } | ||
| 45 | |||
| 46 | Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { | ||
| 47 | return stream; | ||
| 48 | } | ||
| 49 | |||
| 50 | u64 CommandListProcessor::Process(u32 session_id) { | ||
| 51 | const auto start_time_{system->CoreTiming().GetClockTicks()}; | ||
| 52 | const auto command_base{CpuAddr(commands)}; | ||
| 53 | |||
| 54 | if (processed_command_count > 0) { | ||
| 55 | current_processing_time += start_time_ - end_time; | ||
| 56 | } else { | ||
| 57 | start_time = start_time_; | ||
| 58 | current_processing_time = 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | std::string dump{fmt::format("\nSession {}\n", session_id)}; | ||
| 62 | |||
| 63 | for (u32 index = 0; index < command_count; index++) { | ||
| 64 | auto& command{*reinterpret_cast<ICommand*>(commands)}; | ||
| 65 | |||
| 66 | if (command.magic != 0xCAFEBABE) { | ||
| 67 | LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", | ||
| 68 | command.magic); | ||
| 69 | return system->CoreTiming().GetClockTicks() - start_time_; | ||
| 70 | } | ||
| 71 | |||
| 72 | auto current_offset{CpuAddr(commands) - command_base}; | ||
| 73 | |||
| 74 | if (current_offset + command.size > commands_buffer_size) { | ||
| 75 | LOG_ERROR(Service_Audio, | ||
| 76 | "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", | ||
| 77 | commands_buffer_size, | ||
| 78 | CpuAddr(commands) + command.size - sizeof(CommandListHeader)); | ||
| 79 | return system->CoreTiming().GetClockTicks() - start_time_; | ||
| 80 | } | ||
| 81 | |||
| 82 | if (Settings::values.dump_audio_commands) { | ||
| 83 | command.Dump(*this, dump); | ||
| 84 | } | ||
| 85 | |||
| 86 | if (!command.Verify(*this)) { | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | |||
| 90 | if (command.enabled) { | ||
| 91 | command.Process(*this); | ||
| 92 | } else { | ||
| 93 | dump += fmt::format("\tDisabled!\n"); | ||
| 94 | } | ||
| 95 | |||
| 96 | processed_command_count++; | ||
| 97 | commands += command.size; | ||
| 98 | } | ||
| 99 | |||
| 100 | if (Settings::values.dump_audio_commands && dump != last_dump) { | ||
| 101 | LOG_WARNING(Service_Audio, "{}", dump); | ||
| 102 | last_dump = dump; | ||
| 103 | } | ||
| 104 | |||
| 105 | end_time = system->CoreTiming().GetClockTicks(); | ||
| 106 | return end_time - start_time_; | ||
| 107 | } | ||
| 108 | |||
| 109 | } // namespace AudioCore::AudioRenderer::ADSP | ||
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/renderer/adsp/command_list_processor.h new file mode 100644 index 000000000..3f99173e3 --- /dev/null +++ b/src/audio_core/renderer/adsp/command_list_processor.h | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 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 "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | namespace Memory { | ||
| 13 | class Memory; | ||
| 14 | } | ||
| 15 | class System; | ||
| 16 | } // namespace Core | ||
| 17 | |||
| 18 | namespace AudioCore { | ||
| 19 | namespace Sink { | ||
| 20 | class SinkStream; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace AudioRenderer { | ||
| 24 | struct CommandListHeader; | ||
| 25 | |||
| 26 | namespace ADSP { | ||
| 27 | |||
| 28 | /** | ||
| 29 | * A processor for command lists given to the AudioRenderer. | ||
| 30 | */ | ||
| 31 | class CommandListProcessor { | ||
| 32 | public: | ||
| 33 | /** | ||
| 34 | * Initialize the processor. | ||
| 35 | * | ||
| 36 | * @param system_ - The core system. | ||
| 37 | * @param buffer - The command buffer to process. | ||
| 38 | * @param size - The size of the buffer. | ||
| 39 | * @param stream_ - The stream to be used for sending the samples. | ||
| 40 | */ | ||
| 41 | void Initialize(Core::System& system, CpuAddr buffer, u64 size, Sink::SinkStream* stream); | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Set the maximum processing time for this command list. | ||
| 45 | * | ||
| 46 | * @param time - The maximum process time. | ||
| 47 | */ | ||
| 48 | void SetProcessTimeMax(u64 time); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the remaining command count for this list. | ||
| 52 | * | ||
| 53 | * @return The remaining command count. | ||
| 54 | */ | ||
| 55 | u32 GetRemainingCommandCount() const; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Set the command buffer. | ||
| 59 | * | ||
| 60 | * @param buffer - The buffer to use. | ||
| 61 | * @param size - The size of the buffer. | ||
| 62 | */ | ||
| 63 | void SetBuffer(CpuAddr buffer, u64 size); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Get the stream for this command list. | ||
| 67 | * | ||
| 68 | * @return The stream associated with this command list. | ||
| 69 | */ | ||
| 70 | Sink::SinkStream* GetOutputSinkStream() const; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Process the command list. | ||
| 74 | * | ||
| 75 | * @param index - Index of the current command list. | ||
| 76 | * @return The time taken to process. | ||
| 77 | */ | ||
| 78 | u64 Process(u32 session_id); | ||
| 79 | |||
| 80 | /// Core system | ||
| 81 | Core::System* system{}; | ||
| 82 | /// Core memory | ||
| 83 | Core::Memory::Memory* memory{}; | ||
| 84 | /// Stream for the processed samples | ||
| 85 | Sink::SinkStream* stream{}; | ||
| 86 | /// Header info for this command list | ||
| 87 | CommandListHeader* header{}; | ||
| 88 | /// The command buffer | ||
| 89 | u8* commands{}; | ||
| 90 | /// The command buffer size | ||
| 91 | u64 commands_buffer_size{}; | ||
| 92 | /// The maximum processing time alloted | ||
| 93 | u64 max_process_time{}; | ||
| 94 | /// The number of commands in the buffer | ||
| 95 | u32 command_count{}; | ||
| 96 | /// The target sample count for output | ||
| 97 | u32 sample_count{}; | ||
| 98 | /// The target sample rate for output | ||
| 99 | u32 target_sample_rate{}; | ||
| 100 | /// The mixing buffers used by the commands | ||
| 101 | std::span<s32> mix_buffers{}; | ||
| 102 | /// The number of mix buffers | ||
| 103 | u32 buffer_count{}; | ||
| 104 | /// The number of processed commands so far | ||
| 105 | u32 processed_command_count{}; | ||
| 106 | /// The processing start time of this list | ||
| 107 | u64 start_time{}; | ||
| 108 | /// The current processing time for this list | ||
| 109 | u64 current_processing_time{}; | ||
| 110 | /// The end processing time for this list | ||
| 111 | u64 end_time{}; | ||
| 112 | /// Last command list string generated, used for dumping audio commands to console | ||
| 113 | std::string last_dump{}; | ||
| 114 | }; | ||
| 115 | |||
| 116 | } // namespace ADSP | ||
| 117 | } // namespace AudioRenderer | ||
| 118 | } // namespace AudioCore | ||