summaryrefslogtreecommitdiffstats
path: root/src/audio_core/adsp/apps/audio_renderer
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp218
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.h109
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_buffer.h23
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp (renamed from src/audio_core/renderer/adsp/command_list_processor.cpp)29
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_list_processor.h (renamed from src/audio_core/renderer/adsp/command_list_processor.h)21
5 files changed, 369 insertions, 31 deletions
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..972d5e45b
--- /dev/null
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
@@ -0,0 +1,218 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <array>
+#include <chrono>
+
+#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h"
+#include "audio_core/audio_core.h"
+#include "audio_core/common/common.h"
+#include "audio_core/sink/sink.h"
+#include "common/logging/log.h"
+#include "common/microprofile.h"
+#include "common/thread.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+
+MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP_AudioRenderer", MP_RGB(60, 19, 97));
+
+namespace AudioCore::ADSP::AudioRenderer {
+
+AudioRenderer::AudioRenderer(Core::System& system_, Sink::Sink& sink_)
+ : system{system_}, sink{sink_} {}
+
+AudioRenderer::~AudioRenderer() {
+ Stop();
+}
+
+void AudioRenderer::Start() {
+ CreateSinkStreams();
+
+ mailbox.Initialize(AppMailboxId::AudioRenderer);
+
+ main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
+
+ mailbox.Send(Direction::DSP, Message::InitializeOK);
+ if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
+ LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
+ "message response from ADSP!");
+ return;
+ }
+ running = true;
+}
+
+void AudioRenderer::Stop() {
+ if (!running) {
+ return;
+ }
+
+ mailbox.Send(Direction::DSP, Message::Shutdown);
+ if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
+ LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
+ "message response from ADSP!");
+ }
+ main_thread.request_stop();
+ main_thread.join();
+
+ for (auto& stream : streams) {
+ if (stream) {
+ stream->Stop();
+ sink.CloseStream(stream);
+ stream = nullptr;
+ }
+ }
+ running = false;
+}
+
+void AudioRenderer::Signal() {
+ signalled_tick = system.CoreTiming().GetGlobalTimeNs().count();
+ Send(Direction::DSP, Message::Render);
+}
+
+void AudioRenderer::Wait() {
+ auto msg = Receive(Direction::Host);
+ if (msg != Message::RenderResponse) {
+ LOG_ERROR(Service_Audio,
+ "Did not receive the expected render response from the AudioRenderer! Expected "
+ "{}, got {}",
+ Message::RenderResponse, msg);
+ }
+}
+
+void AudioRenderer::Send(Direction dir, u32 message) {
+ mailbox.Send(dir, std::move(message));
+}
+
+u32 AudioRenderer::Receive(Direction dir) {
+ return mailbox.Receive(dir);
+}
+
+void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
+ u64 applet_resource_user_id, bool reset) noexcept {
+ command_buffers[session_id].buffer = buffer;
+ command_buffers[session_id].size = size;
+ command_buffers[session_id].time_limit = time_limit;
+ command_buffers[session_id].applet_resource_user_id = applet_resource_user_id;
+ command_buffers[session_id].reset_buffer = reset;
+}
+
+u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
+ return command_buffers[session_id].remaining_command_count;
+}
+
+void AudioRenderer::ClearRemainCommandCount(s32 session_id) noexcept {
+ command_buffers[session_id].remaining_command_count = 0;
+}
+
+u64 AudioRenderer::GetRenderingStartTick(s32 session_id) const noexcept {
+ return (1000 * command_buffers[session_id].render_time_taken_us) + signalled_tick;
+}
+
+void AudioRenderer::CreateSinkStreams() {
+ u32 channels{sink.GetDeviceChannels()};
+ for (u32 i = 0; i < MaxRendererSessions; i++) {
+ std::string name{fmt::format("ADSP_RenderStream-{}", i)};
+ streams[i] =
+ sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render);
+ streams[i]->SetRingSize(4);
+ }
+}
+
+void AudioRenderer::Main(std::stop_token stop_token) {
+ static constexpr char name[]{"DSP_AudioRenderer_Main"};
+ MicroProfileOnThreadCreate(name);
+ Common::SetCurrentThreadName(name);
+ Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
+
+ // TODO: Create buffer map/unmap thread + mailbox
+ // TODO: Create gMix devices, initialize them here
+
+ if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
+ LOG_ERROR(Service_Audio,
+ "ADSP Audio Renderer -- Failed to receive initialize message from host!");
+ return;
+ }
+
+ mailbox.Send(Direction::Host, Message::InitializeOK);
+
+ // 0.12 seconds (2,304,000 / 19,200,000)
+ constexpr u64 max_process_time{2'304'000ULL};
+
+ while (!stop_token.stop_requested()) {
+ auto msg{mailbox.Receive(Direction::DSP)};
+ switch (msg) {
+ case Message::Shutdown:
+ mailbox.Send(Direction::Host, Message::Shutdown);
+ return;
+
+ case Message::Render: {
+ if (system.IsShuttingDown()) [[unlikely]] {
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ mailbox.Send(Direction::Host, Message::RenderResponse);
+ continue;
+ }
+ std::array<bool, MaxRendererSessions> buffers_reset{};
+ std::array<u64, MaxRendererSessions> render_times_taken{};
+ const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
+
+ for (u32 index = 0; index < MaxRendererSessions; index++) {
+ auto& command_buffer{command_buffers[index]};
+ auto& command_list_processor{command_list_processors[index]};
+
+ // Check this buffer is valid, as it may not be used.
+ if (command_buffer.buffer != 0) {
+ // If there are no remaining commands (from the previous list),
+ // this is a new command list, initialize it.
+ if (command_buffer.remaining_command_count == 0) {
+ command_list_processor.Initialize(system, command_buffer.buffer,
+ command_buffer.size, streams[index]);
+ }
+
+ if (command_buffer.reset_buffer && !buffers_reset[index]) {
+ streams[index]->ClearQueue();
+ buffers_reset[index] = true;
+ }
+
+ u64 max_time{max_process_time};
+ if (index == 1 && command_buffer.applet_resource_user_id ==
+ command_buffers[0].applet_resource_user_id) {
+ max_time = max_process_time - render_times_taken[0];
+ if (render_times_taken[0] > max_process_time) {
+ max_time = 0;
+ }
+ }
+
+ max_time = std::min(command_buffer.time_limit, max_time);
+ command_list_processor.SetProcessTimeMax(max_time);
+
+ if (index == 0) {
+ streams[index]->WaitFreeSpace(stop_token);
+ }
+
+ // Process the command list
+ {
+ MICROPROFILE_SCOPE(Audio_Renderer);
+ render_times_taken[index] =
+ command_list_processor.Process(index) - start_time;
+ }
+
+ const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
+
+ command_buffer.remaining_command_count =
+ command_list_processor.GetRemainingCommandCount();
+ command_buffer.render_time_taken_us = end_time - start_time;
+ }
+ }
+
+ mailbox.Send(Direction::Host, Message::RenderResponse);
+ } break;
+
+ default:
+ LOG_WARNING(Service_Audio,
+ "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
+ break;
+ }
+ }
+}
+
+} // 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..85874d88a
--- /dev/null
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <thread>
+
+#include "audio_core/adsp/apps/audio_renderer/command_buffer.h"
+#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
+#include "audio_core/adsp/mailbox.h"
+#include "common/common_types.h"
+#include "common/polyfill_thread.h"
+#include "common/reader_writer_queue.h"
+#include "common/thread.h"
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace AudioCore {
+namespace Sink {
+class Sink;
+}
+
+namespace ADSP::AudioRenderer {
+
+enum Message : u32 {
+ Invalid = 0,
+ MapUnmap_Map = 1,
+ MapUnmap_MapResponse = 2,
+ MapUnmap_Unmap = 3,
+ MapUnmap_UnmapResponse = 4,
+ MapUnmap_InvalidateCache = 5,
+ MapUnmap_InvalidateCacheResponse = 6,
+ MapUnmap_Shutdown = 7,
+ MapUnmap_ShutdownResponse = 8,
+ InitializeOK = 22,
+ RenderResponse = 32,
+ Render = 42,
+ Shutdown = 52,
+};
+
+/**
+ * The AudioRenderer application running on the ADSP.
+ */
+class AudioRenderer {
+public:
+ explicit AudioRenderer(Core::System& system, Sink::Sink& sink);
+ ~AudioRenderer();
+
+ /**
+ * Start the AudioRenderer.
+ *
+ * @param mailbox The mailbox to use for this session.
+ */
+ void Start();
+
+ /**
+ * Stop the AudioRenderer.
+ */
+ void Stop();
+
+ void Signal();
+ void Wait();
+
+ void Send(Direction dir, u32 message);
+ u32 Receive(Direction dir);
+
+ void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
+ u64 applet_resource_user_id, bool reset) noexcept;
+ u32 GetRemainCommandCount(s32 session_id) const noexcept;
+ void ClearRemainCommandCount(s32 session_id) noexcept;
+ u64 GetRenderingStartTick(s32 session_id) const noexcept;
+
+private:
+ /**
+ * Main AudioRenderer thread, responsible for processing the command lists.
+ */
+ void Main(std::stop_token stop_token);
+
+ /**
+ * Creates the streams which will receive the processed samples.
+ */
+ void CreateSinkStreams();
+
+ /// Core system
+ Core::System& system;
+ /// The output sink the AudioRenderer will send samples to
+ Sink::Sink& sink;
+ /// The active mailbox
+ Mailbox mailbox;
+ /// Main thread
+ std::jthread main_thread{};
+ /// The current state
+ std::atomic<bool> running{};
+ /// Shared memory of input command buffers, set by host, read by DSP
+ std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
+ /// The command lists to process
+ std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{};
+ /// The streams which will receive the processed samples
+ std::array<Sink::SinkStream*, MaxRendererSessions> streams{};
+ /// CPU Tick when the DSP was signalled to process, uses time rather than tick
+ u64 signalled_tick{0};
+};
+
+} // namespace ADSP::AudioRenderer
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "audio_core/common/common.h"
+#include "common/common_types.h"
+
+namespace AudioCore::ADSP::AudioRenderer {
+
+struct CommandBuffer {
+ // Set by the host
+ CpuAddr buffer{};
+ u64 size{};
+ u64 time_limit{};
+ u64 applet_resource_user_id{};
+ bool reset_buffer{};
+ // Set by the DSP
+ u32 remaining_command_count{};
+ u64 render_time_taken_us{};
+};
+
+} // namespace AudioCore::ADSP::AudioRenderer
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp
index 3a0f1ae38..24e4d0496 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.cpp
+++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp
@@ -1,9 +1,9 @@
-// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
-#include "audio_core/renderer/adsp/command_list_processor.h"
+#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
#include "audio_core/renderer/command/command_list_header.h"
#include "audio_core/renderer/command/commands.h"
#include "common/settings.h"
@@ -11,15 +11,15 @@
#include "core/core_timing.h"
#include "core/memory.h"
-namespace AudioCore::AudioRenderer::ADSP {
+namespace AudioCore::ADSP::AudioRenderer {
void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size,
Sink::SinkStream* stream_) {
system = &system_;
memory = &system->ApplicationMemory();
stream = stream_;
- header = reinterpret_cast<CommandListHeader*>(buffer);
- commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader));
+ header = reinterpret_cast<Renderer::CommandListHeader*>(buffer);
+ commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader));
commands_buffer_size = size;
command_count = header->command_count;
sample_count = header->sample_count;
@@ -37,17 +37,12 @@ u32 CommandListProcessor::GetRemainingCommandCount() const {
return command_count - processed_command_count;
}
-void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) {
- commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader));
- commands_buffer_size = size;
-}
-
Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const {
return stream;
}
u64 CommandListProcessor::Process(u32 session_id) {
- const auto start_time_{system->CoreTiming().GetClockTicks()};
+ const auto start_time_{system->CoreTiming().GetGlobalTimeUs().count()};
const auto command_base{CpuAddr(commands)};
if (processed_command_count > 0) {
@@ -60,12 +55,12 @@ u64 CommandListProcessor::Process(u32 session_id) {
std::string dump{fmt::format("\nSession {}\n", session_id)};
for (u32 index = 0; index < command_count; index++) {
- auto& command{*reinterpret_cast<ICommand*>(commands)};
+ auto& command{*reinterpret_cast<Renderer::ICommand*>(commands)};
if (command.magic != 0xCAFEBABE) {
LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}",
command.magic);
- return system->CoreTiming().GetClockTicks() - start_time_;
+ return system->CoreTiming().GetGlobalTimeUs().count() - start_time_;
}
auto current_offset{CpuAddr(commands) - command_base};
@@ -74,8 +69,8 @@ u64 CommandListProcessor::Process(u32 session_id) {
LOG_ERROR(Service_Audio,
"Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}",
commands_buffer_size,
- CpuAddr(commands) + command.size - sizeof(CommandListHeader));
- return system->CoreTiming().GetClockTicks() - start_time_;
+ CpuAddr(commands) + command.size - sizeof(Renderer::CommandListHeader));
+ return system->CoreTiming().GetGlobalTimeUs().count() - start_time_;
}
if (Settings::values.dump_audio_commands) {
@@ -101,8 +96,8 @@ u64 CommandListProcessor::Process(u32 session_id) {
last_dump = dump;
}
- end_time = system->CoreTiming().GetClockTicks();
+ end_time = system->CoreTiming().GetGlobalTimeUs().count();
return end_time - start_time_;
}
-} // namespace AudioCore::AudioRenderer::ADSP
+} // namespace AudioCore::ADSP::AudioRenderer
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h
index d78269e1d..4e5fb793e 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.h
+++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -6,6 +6,7 @@
#include <span>
#include "audio_core/common/common.h"
+#include "audio_core/renderer/command/command_list_header.h"
#include "common/common_types.h"
namespace Core {
@@ -20,10 +21,11 @@ namespace Sink {
class SinkStream;
}
-namespace AudioRenderer {
+namespace Renderer {
struct CommandListHeader;
+}
-namespace ADSP {
+namespace ADSP::AudioRenderer {
/**
* A processor for command lists given to the AudioRenderer.
@@ -55,14 +57,6 @@ public:
u32 GetRemainingCommandCount() const;
/**
- * Set the command buffer.
- *
- * @param buffer - The buffer to use.
- * @param size - The size of the buffer.
- */
- void SetBuffer(CpuAddr buffer, u64 size);
-
- /**
* Get the stream for this command list.
*
* @return The stream associated with this command list.
@@ -85,7 +79,7 @@ public:
/// Stream for the processed samples
Sink::SinkStream* stream{};
/// Header info for this command list
- CommandListHeader* header{};
+ Renderer::CommandListHeader* header{};
/// The command buffer
u8* commands{};
/// The command buffer size
@@ -114,6 +108,5 @@ public:
std::string last_dump{};
};
-} // namespace ADSP
-} // namespace AudioRenderer
+} // namespace ADSP::AudioRenderer
} // namespace AudioCore