mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-02 02:58:46 +00:00
3970 revert fixes crash caused by scheduler 4177 fix mario galaxy and metroid prime 4 not booting Co-authored-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4187 Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
@@ -311,9 +311,8 @@ dispatcher, which will return control to the host.
|
|||||||
SetTerm(IR::Term::LinkBlockFast{next})
|
SetTerm(IR::Term::LinkBlockFast{next})
|
||||||
```
|
```
|
||||||
|
|
||||||
This terminal instruction jumps to the basic block described by `next` unconditionally. This promises guarantees that must be held at runtime - i.e that the program wont hang,
|
This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||||
|
This promises guarantees that must be held at runtime - i.e that the program wont hang,
|
||||||
This is a valid expression if contained within another block (say a `Terminal::If`) and the semantics allow it to be so.
|
|
||||||
|
|
||||||
### Terminal: PopRSBHint
|
### Terminal: PopRSBHint
|
||||||
|
|
||||||
|
|||||||
@@ -29,96 +29,8 @@ 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);
|
|
||||||
|
|
||||||
// TODO: Create buffer map/unmap thread + mailbox
|
main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
|
||||||
// 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) {
|
||||||
@@ -217,4 +129,95 @@ 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
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ 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,9 +37,7 @@ 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_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
|
||||||
Init(stop_token);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
@@ -66,203 +64,206 @@ 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 rc_stop_token) {
|
void OpusDecoder::Init(std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
|
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
|
||||||
|
|
||||||
if (Receive(Direction::DSP, rc_stop_token) != Message::Start) {
|
if (Receive(Direction::DSP, stop_token) != Message::Start) {
|
||||||
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
LOG_ERROR(Service_Audio,
|
||||||
|
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Main OpusDecoder thread, responsible for processing the incoming Opus packets.
|
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
||||||
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
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ 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,17 +277,7 @@ private:
|
|||||||
state.store(State::Processing);
|
state.store(State::Processing);
|
||||||
evt_processing->Signal(system.Kernel());
|
evt_processing->Signal(system.Kernel());
|
||||||
|
|
||||||
worker = std::thread([this]() {
|
worker = std::thread(&IScanRequest::WorkerThread, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +308,21 @@ 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);
|
||||||
|
|||||||
@@ -17,14 +17,10 @@ bool TranslatorVisitor::arm_B(Cond cond, Imm<24> imm24) {
|
|||||||
if (!ArmConditionPassed(cond)) {
|
if (!ArmConditionPassed(cond)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
|
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
|
||||||
const auto new_location = ir.current_location.AdvancePC(imm32);
|
const auto new_location = ir.current_location.AdvancePC(imm32);
|
||||||
// pattern match => b .
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
if (imm32 == 0) {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +35,7 @@ bool TranslatorVisitor::arm_BL(Cond cond, Imm<24> imm24) {
|
|||||||
|
|
||||||
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
|
const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8;
|
||||||
const auto new_location = ir.current_location.AdvancePC(imm32);
|
const auto new_location = ir.current_location.AdvancePC(imm32);
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +46,7 @@ bool TranslatorVisitor::arm_BLX_imm(bool H, Imm<24> imm24) {
|
|||||||
|
|
||||||
const u32 imm32 = mcl::bit::sign_extend<26, u32>((imm24.ZeroExtend() << 2)) + (H ? 2 : 0) + 8;
|
const u32 imm32 = mcl::bit::sign_extend<26, u32>((imm24.ZeroExtend() << 2)) + (H ? 2 : 0) + 8;
|
||||||
const auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true);
|
const auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true);
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,10 +56,11 @@ bool TranslatorVisitor::arm_MSR_imm(Cond cond, unsigned mask, int rotate, Imm<8>
|
|||||||
if (write_e) {
|
if (write_e) {
|
||||||
const bool E = (imm32 & 0x00000200) != 0;
|
const bool E = (imm32 & 0x00000200) != 0;
|
||||||
if (E != ir.current_location.EFlag()) {
|
if (E != ir.current_location.EFlag()) {
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)});
|
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ bool TranslatorVisitor::arm_RFE() {
|
|||||||
|
|
||||||
// SETEND <endian_specifier>
|
// SETEND <endian_specifier>
|
||||||
bool TranslatorVisitor::arm_SETEND(bool E) {
|
bool TranslatorVisitor::arm_SETEND(bool E) {
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)});
|
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -803,7 +803,7 @@ bool TranslatorVisitor::thumb16_SETEND(bool E) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()});
|
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,8 +906,9 @@ bool TranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Re
|
|||||||
ir.SetCheckBit(ir.IsZero(rn));
|
ir.SetCheckBit(ir.IsZero(rn));
|
||||||
|
|
||||||
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
|
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
|
||||||
const auto skip = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()};
|
const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2).AdvanceIT()};
|
||||||
const auto branch = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm + 4).AdvanceIT()};
|
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(imm + 4).AdvanceIT()};
|
||||||
|
|
||||||
if (nonzero) {
|
if (nonzero) {
|
||||||
return std::make_pair(skip, branch);
|
return std::make_pair(skip, branch);
|
||||||
} else {
|
} else {
|
||||||
@@ -974,10 +975,10 @@ bool TranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
|
||||||
const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()};
|
const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
||||||
const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()};
|
const auto else_location = ir.current_location.AdvancePC(2).AdvanceIT();
|
||||||
|
|
||||||
ir.SetTerm(IR::Term::If{cond, then_, else_});
|
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,12 +990,8 @@ bool TranslatorVisitor::thumb16_B_t2(Imm<11> imm11) {
|
|||||||
|
|
||||||
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
|
||||||
const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
||||||
// pattern (b +#0)
|
|
||||||
if (imm32 == 0) {
|
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
||||||
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{next_location});
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ bool TranslatorVisitor::thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j
|
|||||||
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
|
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
|
||||||
|
|
||||||
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
|
||||||
auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
const auto new_location = ir.current_location
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
.AdvancePC(imm32)
|
||||||
|
.AdvanceIT();
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +47,11 @@ bool TranslatorVisitor::thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1>
|
|||||||
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
|
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
|
||||||
|
|
||||||
const s32 imm32 = static_cast<s32>(concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1);
|
const s32 imm32 = static_cast<s32>(concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1);
|
||||||
const auto new_location = ir.current_location.SetPC(ir.AlignPC(4) + imm32).SetTFlag(false).AdvanceIT();
|
const auto new_location = ir.current_location
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
.SetPC(ir.AlignPC(4) + imm32)
|
||||||
|
.SetTFlag(false)
|
||||||
|
.AdvanceIT();
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +64,10 @@ bool TranslatorVisitor::thumb32_B(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Im
|
|||||||
}
|
}
|
||||||
|
|
||||||
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4);
|
||||||
auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
const auto new_location = ir.current_location
|
||||||
// Pattern to halt execution (b +#0)
|
.AdvancePC(imm32)
|
||||||
if (imm32 == 0) {
|
.AdvanceIT();
|
||||||
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
ir.SetTerm(IR::Term::LinkBlock{new_location});
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{new_location});
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,9 +78,13 @@ bool TranslatorVisitor::thumb32_B_cond(Imm<1> S, Cond cond, Imm<6> hi, Imm<1> i1
|
|||||||
|
|
||||||
// Note: i1 and i2 were not inverted from encoding and are opposite compared to the other B instructions.
|
// Note: i1 and i2 were not inverted from encoding and are opposite compared to the other B instructions.
|
||||||
const s32 imm32 = static_cast<s32>((concatenate(S, i2, i1, hi, lo).SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((concatenate(S, i2, i1, hi, lo).SignExtend<u32>() << 1) + 4);
|
||||||
const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()};
|
const auto then_location = ir.current_location
|
||||||
const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).AdvanceIT()};
|
.AdvancePC(imm32)
|
||||||
ir.SetTerm(IR::Term::If{cond, then_, else_});
|
.AdvanceIT();
|
||||||
|
const auto else_location = ir.current_location
|
||||||
|
.AdvancePC(4)
|
||||||
|
.AdvanceIT();
|
||||||
|
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ bool TranslatorVisitor::B_cond(Imm<19> imm19, Cond cond) {
|
|||||||
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
||||||
const u64 target = ir.PC() + offset;
|
const u64 target = ir.PC() + offset;
|
||||||
|
|
||||||
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
|
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
|
||||||
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
|
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
|
||||||
ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail});
|
ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -26,9 +26,9 @@ bool TranslatorVisitor::B_uncond(Imm<26> imm26) {
|
|||||||
// Pattern to halt execution (B .)
|
// Pattern to halt execution (B .)
|
||||||
if (target == ir.PC()) {
|
if (target == ir.PC()) {
|
||||||
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
|
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
|
||||||
} else {
|
return false;
|
||||||
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)});
|
|
||||||
}
|
}
|
||||||
|
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) {
|
|||||||
ir.SetCheckBit(ir.IsZero(operand1));
|
ir.SetCheckBit(ir.IsZero(operand1));
|
||||||
|
|
||||||
const u64 target = ir.PC() + offset;
|
const u64 target = ir.PC() + offset;
|
||||||
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
|
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
|
||||||
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
|
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
|
||||||
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -94,8 +94,8 @@ bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
|
|||||||
ir.SetCheckBit(ir.IsZero(operand1));
|
ir.SetCheckBit(ir.IsZero(operand1));
|
||||||
|
|
||||||
const u64 target = ir.PC() + offset;
|
const u64 target = ir.PC() + offset;
|
||||||
const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
|
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
|
||||||
const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
|
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
|
||||||
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -110,8 +110,8 @@ bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
|
|||||||
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
||||||
|
|
||||||
const u64 target = ir.PC() + offset;
|
const u64 target = ir.PC() + offset;
|
||||||
const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
|
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
|
||||||
const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
|
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
|
||||||
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -126,8 +126,8 @@ bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
|
|||||||
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
|
||||||
|
|
||||||
const u64 target = ir.PC() + offset;
|
const u64 target = ir.PC() + offset;
|
||||||
const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)};
|
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
|
||||||
const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)};
|
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
|
||||||
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,57 +60,7 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
|
|||||||
|
|
||||||
AcquireNewChunk();
|
AcquireNewChunk();
|
||||||
AllocateWorkerCommandBuffer();
|
AllocateWorkerCommandBuffer();
|
||||||
worker_thread = std::jthread([this](std::stop_token stop_token) {
|
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(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;
|
||||||
@@ -248,6 +198,59 @@ 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,6 +251,8 @@ 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);
|
||||||
|
|||||||
Reference in New Issue
Block a user