mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-25 00:17:00 +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();
|
||||
|
||||
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);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -82,11 +82,6 @@ public:
|
|||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_co
|
|||
} // namespace
|
||||
|
||||
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() {
|
||||
|
|
@ -64,206 +66,203 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token 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");
|
||||
|
||||
if (Receive(Direction::DSP, stop_token) != Message::Start) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||
if (Receive(Direction::DSP, rc_stop_token) != Message::Start) {
|
||||
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||
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;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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-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.
|
||||
*/
|
||||
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& system;
|
||||
|
|
|
|||
|
|
@ -277,7 +277,17 @@ private:
|
|||
state.store(State::Processing);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -308,21 +318,6 @@ private:
|
|||
|
||||
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) {
|
||||
worker_result.store(rc);
|
||||
state.store(State::Finished);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,57 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
|
|||
|
||||
AcquireNewChunk();
|
||||
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;
|
||||
|
|
@ -187,59 +237,6 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) {
|
|||
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() {
|
||||
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
|
||||
current_cmdbuf.Begin({
|
||||
|
|
|
|||
|
|
@ -251,8 +251,6 @@ private:
|
|||
bool needs_state_enable_refresh = false;
|
||||
};
|
||||
|
||||
void WorkerThread(std::stop_token stop_token);
|
||||
|
||||
void AllocateWorkerCommandBuffer();
|
||||
|
||||
u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue