mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-06-01 18:37:04 +02:00
[audio_core, hle, video_core] force inline of functions that only contain thread loops (#3970)
traditionally, when doing jthread:
```
jthread() calls function parameter operator()() with args
function operator()() calls the code within
code within is, say { ThreadMain(); }
3 calls because why not
```
now this just makes it be 2 calls, mainly benefits non-LTO builds
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3970
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
parent
d761ecba8c
commit
37b5cf6003
7 changed files with 349 additions and 369 deletions
|
|
@ -29,8 +29,96 @@ void AudioRenderer::Start() {
|
||||||
CreateSinkStreams();
|
CreateSinkStreams();
|
||||||
|
|
||||||
mailbox.Initialize(AppMailboxId::AudioRenderer);
|
mailbox.Initialize(AppMailboxId::AudioRenderer);
|
||||||
|
// Main AudioRenderer thread, responsible for processing the command lists.
|
||||||
|
main_thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
|
||||||
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||||
|
|
||||||
main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
|
// 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()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
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.process,
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
||||||
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
||||||
|
|
@ -129,95 +217,4 @@ void AudioRenderer::CreateSinkStreams() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Main(std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
|
|
||||||
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()) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
||||||
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.process,
|
|
||||||
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
|
|
||||||
{
|
|
||||||
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
|
} // namespace AudioCore::ADSP::AudioRenderer
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
|
@ -82,11 +82,6 @@ public:
|
||||||
u64 GetRenderingStartTick(s32 session_id) const noexcept;
|
u64 GetRenderingStartTick(s32 session_id) const noexcept;
|
||||||
|
|
||||||
private:
|
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.
|
* Creates the streams which will receive the processed samples.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_co
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
||||||
init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
|
init_thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Init(stop_token);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
|
|
@ -64,206 +66,203 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
|
||||||
return mailbox.Receive(dir, stop_token);
|
return mailbox.Receive(dir, stop_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Init(std::stop_token stop_token) {
|
void OpusDecoder::Init(std::stop_token rc_stop_token) {
|
||||||
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
|
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
|
||||||
|
|
||||||
if (Receive(Direction::DSP, stop_token) != Message::Start) {
|
if (Receive(Direction::DSP, rc_stop_token) != Message::Start) {
|
||||||
LOG_ERROR(Service_Audio,
|
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||||
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
// Main OpusDecoder thread, responsible for processing the incoming Opus packets.
|
||||||
|
main_thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
auto msg = Receive(Direction::DSP, stop_token);
|
||||||
|
switch (msg) {
|
||||||
|
case Shutdown:
|
||||||
|
Send(Direction::Host, Message::ShutdownOK);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case GetWorkBufferSize: {
|
||||||
|
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
|
|
||||||
|
ASSERT(IsValidChannelCount(channel_count));
|
||||||
|
|
||||||
|
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
|
||||||
|
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case InitializeDecodeObject: {
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
|
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
||||||
|
|
||||||
|
ASSERT(sample_rate >= 0);
|
||||||
|
ASSERT(IsValidChannelCount(channel_count));
|
||||||
|
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
|
||||||
|
|
||||||
|
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||||
|
shared_memory->dsp_return_data[0] =
|
||||||
|
decoder_object.InitializeDecoder(sample_rate, channel_count);
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ShutdownDecodeObject: {
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
|
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||||
|
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DecodeInterleaved: {
|
||||||
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
auto input_data = shared_memory->host_send_data[1];
|
||||||
|
auto input_data_size = shared_memory->host_send_data[2];
|
||||||
|
auto output_data = shared_memory->host_send_data[3];
|
||||||
|
auto output_data_size = shared_memory->host_send_data[4];
|
||||||
|
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||||
|
auto reset_requested = shared_memory->host_send_data[6];
|
||||||
|
|
||||||
|
u32 decoded_samples{0};
|
||||||
|
|
||||||
|
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
||||||
|
s32 error_code{OPUS_OK};
|
||||||
|
if (reset_requested) {
|
||||||
|
error_code = decoder_object.ResetDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_code == OPUS_OK) {
|
||||||
|
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
||||||
|
input_data, input_data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_code == OPUS_OK) {
|
||||||
|
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
||||||
|
error_code = OPUS_INVALID_PACKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
shared_memory->dsp_return_data[0] = error_code;
|
||||||
|
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||||
|
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case MapMemory: {
|
||||||
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
Send(Direction::Host, Message::MapMemoryOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case UnmapMemory: {
|
||||||
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case GetWorkBufferSizeForMultiStream: {
|
||||||
|
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
|
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
||||||
|
|
||||||
|
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
||||||
|
|
||||||
|
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
||||||
|
total_stream_count, stereo_stream_count);
|
||||||
|
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case InitializeMultiStreamDecodeObject: {
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
|
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
||||||
|
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
|
||||||
|
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
|
||||||
|
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
|
||||||
|
// mappings, but [6] is never set, and there is not enough room in the argument data for
|
||||||
|
// more than 40 channels, when 255 are possible.
|
||||||
|
// It also means the mapping values are undefined, though likely always 0,
|
||||||
|
// and the mappings given by the game are ignored. The mappings are copied to this
|
||||||
|
// dedicated buffer host side, so let's do as intended.
|
||||||
|
auto mappings = shared_memory->channel_mapping.data();
|
||||||
|
|
||||||
|
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
||||||
|
ASSERT(sample_rate >= 0);
|
||||||
|
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
||||||
|
total_stream_count, stereo_stream_count));
|
||||||
|
|
||||||
|
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||||
|
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
|
||||||
|
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ShutdownMultiStreamDecodeObject: {
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
|
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||||
|
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DecodeInterleavedForMultiStream: {
|
||||||
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
|
auto input_data = shared_memory->host_send_data[1];
|
||||||
|
auto input_data_size = shared_memory->host_send_data[2];
|
||||||
|
auto output_data = shared_memory->host_send_data[3];
|
||||||
|
auto output_data_size = shared_memory->host_send_data[4];
|
||||||
|
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
||||||
|
auto reset_requested = shared_memory->host_send_data[6];
|
||||||
|
|
||||||
|
u32 decoded_samples{0};
|
||||||
|
|
||||||
|
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
||||||
|
s32 error_code{OPUS_OK};
|
||||||
|
if (reset_requested) {
|
||||||
|
error_code = decoder_object.ResetDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_code == OPUS_OK) {
|
||||||
|
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
||||||
|
input_data, input_data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_code == OPUS_OK) {
|
||||||
|
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
||||||
|
error_code = OPUS_INVALID_PACKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
shared_memory->dsp_return_data[0] = error_code;
|
||||||
|
shared_memory->dsp_return_data[1] = decoded_samples;
|
||||||
|
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
||||||
|
|
||||||
|
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
running = true;
|
running = true;
|
||||||
Send(Direction::Host, Message::StartOK);
|
Send(Direction::Host, Message::StartOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Main(std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
|
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
auto msg = Receive(Direction::DSP, stop_token);
|
|
||||||
switch (msg) {
|
|
||||||
case Shutdown:
|
|
||||||
Send(Direction::Host, Message::ShutdownOK);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case GetWorkBufferSize: {
|
|
||||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
|
||||||
|
|
||||||
ASSERT(IsValidChannelCount(channel_count));
|
|
||||||
|
|
||||||
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
|
|
||||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case InitializeDecodeObject: {
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
|
||||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
|
||||||
|
|
||||||
ASSERT(sample_rate >= 0);
|
|
||||||
ASSERT(IsValidChannelCount(channel_count));
|
|
||||||
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
|
|
||||||
|
|
||||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
|
||||||
shared_memory->dsp_return_data[0] =
|
|
||||||
decoder_object.InitializeDecoder(sample_rate, channel_count);
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ShutdownDecodeObject: {
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
|
|
||||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
|
||||||
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case DecodeInterleaved: {
|
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
auto input_data = shared_memory->host_send_data[1];
|
|
||||||
auto input_data_size = shared_memory->host_send_data[2];
|
|
||||||
auto output_data = shared_memory->host_send_data[3];
|
|
||||||
auto output_data_size = shared_memory->host_send_data[4];
|
|
||||||
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
|
||||||
auto reset_requested = shared_memory->host_send_data[6];
|
|
||||||
|
|
||||||
u32 decoded_samples{0};
|
|
||||||
|
|
||||||
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
|
|
||||||
s32 error_code{OPUS_OK};
|
|
||||||
if (reset_requested) {
|
|
||||||
error_code = decoder_object.ResetDecoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_code == OPUS_OK) {
|
|
||||||
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
|
||||||
input_data, input_data_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_code == OPUS_OK) {
|
|
||||||
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
|
||||||
error_code = OPUS_INVALID_PACKET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
|
||||||
shared_memory->dsp_return_data[0] = error_code;
|
|
||||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
|
||||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MapMemory: {
|
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
Send(Direction::Host, Message::MapMemoryOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case UnmapMemory: {
|
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case GetWorkBufferSizeForMultiStream: {
|
|
||||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
|
||||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
|
||||||
|
|
||||||
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
|
||||||
|
|
||||||
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
|
||||||
total_stream_count, stereo_stream_count);
|
|
||||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case InitializeMultiStreamDecodeObject: {
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
|
||||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
|
|
||||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
|
|
||||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
|
|
||||||
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
|
|
||||||
// mappings, but [6] is never set, and there is not enough room in the argument data for
|
|
||||||
// more than 40 channels, when 255 are possible.
|
|
||||||
// It also means the mapping values are undefined, though likely always 0,
|
|
||||||
// and the mappings given by the game are ignored. The mappings are copied to this
|
|
||||||
// dedicated buffer host side, so let's do as intended.
|
|
||||||
auto mappings = shared_memory->channel_mapping.data();
|
|
||||||
|
|
||||||
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
|
|
||||||
ASSERT(sample_rate >= 0);
|
|
||||||
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
|
|
||||||
total_stream_count, stereo_stream_count));
|
|
||||||
|
|
||||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
|
||||||
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
|
|
||||||
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ShutdownMultiStreamDecodeObject: {
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
|
||||||
|
|
||||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
|
||||||
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case DecodeInterleavedForMultiStream: {
|
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
|
||||||
auto input_data = shared_memory->host_send_data[1];
|
|
||||||
auto input_data_size = shared_memory->host_send_data[2];
|
|
||||||
auto output_data = shared_memory->host_send_data[3];
|
|
||||||
auto output_data_size = shared_memory->host_send_data[4];
|
|
||||||
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
|
|
||||||
auto reset_requested = shared_memory->host_send_data[6];
|
|
||||||
|
|
||||||
u32 decoded_samples{0};
|
|
||||||
|
|
||||||
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
|
|
||||||
s32 error_code{OPUS_OK};
|
|
||||||
if (reset_requested) {
|
|
||||||
error_code = decoder_object.ResetDecoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_code == OPUS_OK) {
|
|
||||||
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
|
|
||||||
input_data, input_data_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_code == OPUS_OK) {
|
|
||||||
if (final_range && decoder_object.GetFinalRange() != final_range) {
|
|
||||||
error_code = OPUS_INVALID_PACKET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end_time = system.CoreTiming().GetGlobalTimeUs();
|
|
||||||
shared_memory->dsp_return_data[0] = error_code;
|
|
||||||
shared_memory->dsp_return_data[1] = decoded_samples;
|
|
||||||
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
|
|
||||||
|
|
||||||
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore::ADSP::OpusDecoder
|
} // namespace AudioCore::ADSP::OpusDecoder
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -69,10 +72,6 @@ private:
|
||||||
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
||||||
*/
|
*/
|
||||||
void Init(std::stop_token stop_token);
|
void Init(std::stop_token stop_token);
|
||||||
/**
|
|
||||||
* Main OpusDecoder thread, responsible for processing the incoming Opus packets.
|
|
||||||
*/
|
|
||||||
void Main(std::stop_token stop_token);
|
|
||||||
|
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,17 @@ private:
|
||||||
state.store(State::Processing);
|
state.store(State::Processing);
|
||||||
evt_processing->Signal();
|
evt_processing->Signal();
|
||||||
|
|
||||||
worker = std::thread(&IScanRequest::WorkerThread, this);
|
worker = std::thread([this]() {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
scan_results = Network::ScanWifiNetworks(3s);
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{g_scan_mtx};
|
||||||
|
g_last_scan_results = scan_results;
|
||||||
|
}
|
||||||
|
// choose result code
|
||||||
|
const bool ok = !scan_results.empty();
|
||||||
|
Finish(ok ? ResultSuccess : ResultPendingConnection);
|
||||||
|
});
|
||||||
IPC::ResponseBuilder{ctx, 2}.Push(ResultSuccess);
|
IPC::ResponseBuilder{ctx, 2}.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,21 +318,6 @@ private:
|
||||||
|
|
||||||
enum class State { Idle, Processing, Finished };
|
enum class State { Idle, Processing, Finished };
|
||||||
|
|
||||||
void WorkerThread() {
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
scan_results = Network::ScanWifiNetworks(3s);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lk{g_scan_mtx};
|
|
||||||
g_last_scan_results = scan_results;
|
|
||||||
}
|
|
||||||
|
|
||||||
// choose result code
|
|
||||||
const bool ok = !scan_results.empty();
|
|
||||||
Finish(ok ? ResultSuccess : ResultPendingConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Finish(Result rc) {
|
void Finish(Result rc) {
|
||||||
worker_result.store(rc);
|
worker_result.store(rc);
|
||||||
state.store(State::Finished);
|
state.store(State::Finished);
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,57 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
|
||||||
|
|
||||||
AcquireNewChunk();
|
AcquireNewChunk();
|
||||||
AllocateWorkerCommandBuffer();
|
AllocateWorkerCommandBuffer();
|
||||||
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
|
worker_thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("VulkanWorker");
|
||||||
|
const auto TryPopQueue{[this](auto& work) -> bool {
|
||||||
|
if (work_queue.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
work = std::move(work_queue.front());
|
||||||
|
work_queue.pop();
|
||||||
|
event_cv.notify_all();
|
||||||
|
return true;
|
||||||
|
}};
|
||||||
|
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
std::unique_ptr<CommandChunk> work;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lk{queue_mutex};
|
||||||
|
|
||||||
|
// Wait for work.
|
||||||
|
event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); });
|
||||||
|
|
||||||
|
// If we've been asked to stop, we're done.
|
||||||
|
if (stop_token.stop_requested()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exchange lock ownership so that we take the execution lock before
|
||||||
|
// the queue lock goes out of scope. This allows us to force execution
|
||||||
|
// to complete in the next step.
|
||||||
|
std::exchange(lk, std::unique_lock{execution_mutex});
|
||||||
|
|
||||||
|
// Perform the work, tracking whether the chunk was a submission
|
||||||
|
// before executing.
|
||||||
|
const bool has_submit = work->HasSubmit();
|
||||||
|
work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
|
||||||
|
|
||||||
|
// If the chunk was a submission, reallocate the command buffer.
|
||||||
|
if (has_submit) {
|
||||||
|
AllocateWorkerCommandBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock rl{reserve_mutex};
|
||||||
|
|
||||||
|
// Recycle the chunk back to the reserve.
|
||||||
|
chunk_reserve.emplace_back(std::move(work));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Scheduler::~Scheduler() = default;
|
Scheduler::~Scheduler() = default;
|
||||||
|
|
@ -187,59 +237,6 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::WorkerThread(std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("VulkanWorker");
|
|
||||||
|
|
||||||
const auto TryPopQueue{[this](auto& work) -> bool {
|
|
||||||
if (work_queue.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
work = std::move(work_queue.front());
|
|
||||||
work_queue.pop();
|
|
||||||
event_cv.notify_all();
|
|
||||||
return true;
|
|
||||||
}};
|
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
std::unique_ptr<CommandChunk> work;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::unique_lock lk{queue_mutex};
|
|
||||||
|
|
||||||
// Wait for work.
|
|
||||||
event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); });
|
|
||||||
|
|
||||||
// If we've been asked to stop, we're done.
|
|
||||||
if (stop_token.stop_requested()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exchange lock ownership so that we take the execution lock before
|
|
||||||
// the queue lock goes out of scope. This allows us to force execution
|
|
||||||
// to complete in the next step.
|
|
||||||
std::exchange(lk, std::unique_lock{execution_mutex});
|
|
||||||
|
|
||||||
// Perform the work, tracking whether the chunk was a submission
|
|
||||||
// before executing.
|
|
||||||
const bool has_submit = work->HasSubmit();
|
|
||||||
work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
|
|
||||||
|
|
||||||
// If the chunk was a submission, reallocate the command buffer.
|
|
||||||
if (has_submit) {
|
|
||||||
AllocateWorkerCommandBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock rl{reserve_mutex};
|
|
||||||
|
|
||||||
// Recycle the chunk back to the reserve.
|
|
||||||
chunk_reserve.emplace_back(std::move(work));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::AllocateWorkerCommandBuffer() {
|
void Scheduler::AllocateWorkerCommandBuffer() {
|
||||||
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
|
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
|
||||||
current_cmdbuf.Begin({
|
current_cmdbuf.Begin({
|
||||||
|
|
|
||||||
|
|
@ -251,8 +251,6 @@ private:
|
||||||
bool needs_state_enable_refresh = false;
|
bool needs_state_enable_refresh = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void WorkerThread(std::stop_token stop_token);
|
|
||||||
|
|
||||||
void AllocateWorkerCommandBuffer();
|
void AllocateWorkerCommandBuffer();
|
||||||
|
|
||||||
u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue