Compare commits

..

18 Commits

Author SHA1 Message Date
lizzie ca9dc2da35 Fix license headers 2026-09-28 06:16:38 +00:00
lizzie 33e2e0cf57 2026-09-23 07:42:01
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 737535dc2b 2026-09-23 02:07:39
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie ccb257c729 2026-09-23 01:28:59
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie f9292fa4ac 2026-09-23 01:18:14
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie b5b59f2659 2026-09-22 04:33:30
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie e18e8b5995 2026-09-22 04:25:09
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 214d7772b4 2026-09-21 02:08:29
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 08585f03de 2026-09-21 02:07:30
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 4bb5ab4d83 Fix license headers 2026-09-28 06:16:25 +00:00
lizzie 6acf3c496a Fix license headers 2026-09-28 06:16:25 +00:00
lizzie c9c5e5c677 2026-09-21 00:39:13
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 86761a1892 Fix license headers 2026-09-28 06:16:25 +00:00
lizzie 261cf66e58 2026-09-20 13:20:40
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 2a991fafe9 2026-09-20 13:02:52
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie 338cf2a532 2026-09-20 12:52:02
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
lizzie e85ce56b11 Fix license headers 2026-09-28 06:16:25 +00:00
lizzie e853a577e3 2026-09-20 07:03:36
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-28 06:16:25 +00:00
72 changed files with 1197 additions and 837 deletions
+6 -1
View File
@@ -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
@@ -19,6 +19,11 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
} }
} }
void ADSP::Shutdown() {
audio_renderer->Stop();
opus_decoder->Shutdown();
}
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
return *audio_renderer; return *audio_renderer;
} }
+2 -1
View File
@@ -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
@@ -45,6 +45,7 @@ public:
explicit ADSP(Core::System& system, Sink::Sink& sink); explicit ADSP(Core::System& system, Sink::Sink& sink);
~ADSP() = default; ~ADSP() = default;
void Shutdown();
AudioRenderer::AudioRenderer& AudioRenderer(); AudioRenderer::AudioRenderer& AudioRenderer();
OpusDecoder::OpusDecoder& OpusDecoder(); OpusDecoder::OpusDecoder& OpusDecoder();
@@ -34,26 +34,19 @@ void AudioRenderer::Start() {
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) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
"message response from ADSP!");
return;
} }
running = true;
} }
void AudioRenderer::Stop() { void AudioRenderer::Stop() {
if (!running) { if (main_thread.joinable()) {
return; main_thread.request_stop();
mailbox.Send(Direction::DSP, Message::Shutdown);
if (mailbox.Receive(Direction::Host, main_thread.get_stop_token()) != Message::Shutdown) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
}
main_thread.join();
} }
mailbox.Send(Direction::DSP, Message::Shutdown);
if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
"message response from ADSP!");
}
main_thread.request_stop();
main_thread.join();
for (auto& stream : streams) { for (auto& stream : streams) {
if (stream) { if (stream) {
stream->Stop(); stream->Stop();
@@ -61,7 +54,6 @@ void AudioRenderer::Stop() {
stream = nullptr; stream = nullptr;
} }
} }
running = false;
} }
void AudioRenderer::Signal() { void AudioRenderer::Signal() {
@@ -69,13 +61,12 @@ void AudioRenderer::Signal() {
Send(Direction::DSP, Message::Render); Send(Direction::DSP, Message::Render);
} }
void AudioRenderer::Wait() { void AudioRenderer::Wait(std::stop_token stop_token) {
auto msg = Receive(Direction::Host); auto msg = mailbox.Receive(Direction::Host, stop_token);
if (msg != Message::RenderResponse) { if (msg != Message::RenderResponse) {
LOG_ERROR(Service_Audio, LOG_ERROR(Service_Audio,
"Did not receive the expected render response from the AudioRenderer! Expected " "Did not receive the expected render response from the AudioRenderer! Expected "
"{}, got {}", "{}, got {}", Message::RenderResponse, msg);
Message::RenderResponse, msg);
} }
PostDSPClearCommandBuffer(); PostDSPClearCommandBuffer();
} }
@@ -147,7 +138,9 @@ void AudioRenderer::Main(std::stop_token stop_token) {
constexpr u64 max_process_time{2'304'000ULL}; constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg{mailbox.Receive(Direction::DSP)}; auto msg = mailbox.Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Message::Shutdown: case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown); mailbox.Send(Direction::Host, Message::Shutdown);
@@ -63,13 +63,15 @@ public:
*/ */
void Start(); void Start();
void NotifyShutdown();
/** /**
* Stop the AudioRenderer. * Stop the AudioRenderer.
*/ */
void Stop(); void Stop();
void Signal(); void Signal();
void Wait(); void Wait(std::stop_token stop_token);
void Send(Direction dir, u32 message); void Send(Direction dir, u32 message);
u32 Receive(Direction dir); u32 Receive(Direction dir);
@@ -102,8 +104,6 @@ private:
Mailbox mailbox; Mailbox mailbox;
/// Main thread /// Main thread
std::jthread main_thread{}; std::jthread main_thread{};
/// The current state
std::atomic<bool> running{};
/// Shared memory of input command buffers, set by host, read by DSP /// Shared memory of input command buffers, set by host, read by DSP
std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
/// The command lists to process /// The command lists to process
+33 -23
View File
@@ -41,19 +41,16 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
} }
OpusDecoder::~OpusDecoder() { OpusDecoder::~OpusDecoder() {
if (!running) { if (main_thread.joinable()) {
// Shutdown the thread
Send(Direction::DSP, Message::Shutdown);
main_thread.request_stop();
auto msg = Receive(Direction::Host, main_thread.get_stop_token());
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
main_thread.join();
} else {
init_thread.request_stop(); init_thread.request_stop();
return;
} }
// Shutdown the thread
Send(Direction::DSP, Message::Shutdown);
auto msg = Receive(Direction::Host);
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
Message::ShutdownOK, msg);
main_thread.request_stop();
main_thread.join();
running = false;
} }
void OpusDecoder::Send(Direction dir, u32 message) { void OpusDecoder::Send(Direction dir, u32 message) {
@@ -73,7 +70,6 @@ void OpusDecoder::Init(std::stop_token stop_token) {
return; return;
} }
main_thread = std::jthread([this](std::stop_token st) { Main(st); }); main_thread = std::jthread([this](std::stop_token st) { Main(st); });
running = true;
Send(Direction::Host, Message::StartOK); Send(Direction::Host, Message::StartOK);
} }
@@ -82,12 +78,15 @@ void OpusDecoder::Main(std::stop_token stop_token) {
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token); auto msg = Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Shutdown: case Message::Shutdown:
Send(Direction::Host, Message::ShutdownOK); Send(Direction::Host, Message::ShutdownOK);
return; return;
case GetWorkBufferSize: { case Message::GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]); auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count)); ASSERT(IsValidChannelCount(channel_count));
@@ -96,7 +95,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeOK); Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break; } break;
case InitializeDecodeObject: { case Message::InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1]; auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -113,7 +112,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeDecodeObjectOK); Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break; } break;
case ShutdownDecodeObject: { case Message::ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -123,7 +122,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownDecodeObjectOK); Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break; } break;
case DecodeInterleaved: { case Message::DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs(); auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
@@ -161,19 +160,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::DecodeInterleavedOK); Send(Direction::Host, Message::DecodeInterleavedOK);
} break; } break;
case MapMemory: { case Message::MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK); Send(Direction::Host, Message::MapMemoryOK);
} break; } break;
case UnmapMemory: { case Message::UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK); Send(Direction::Host, Message::UnmapMemoryOK);
} break; } break;
case GetWorkBufferSizeForMultiStream: { case Message::GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]); 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]); auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
@@ -184,7 +183,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break; } break;
case InitializeMultiStreamDecodeObject: { case Message::InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1]; auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -211,7 +210,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break; } break;
case ShutdownMultiStreamDecodeObject: { case Message::ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -221,7 +220,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break; } break;
case DecodeInterleavedForMultiStream: { case Message::DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs(); auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
@@ -266,4 +265,15 @@ void OpusDecoder::Main(std::stop_token stop_token) {
} }
} }
void OpusDecoder::Shutdown() {
if (init_thread.joinable()) {
init_thread.request_stop();
init_thread.join(); //must explicitly wait for init to join
}
if (main_thread.joinable()) {
main_thread.request_stop();
Send(Direction::DSP, Message::Shutdown);
}
}
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder
+3 -3
View File
@@ -57,7 +57,7 @@ public:
~OpusDecoder(); ~OpusDecoder();
bool IsRunning() const noexcept { bool IsRunning() const noexcept {
return running; return main_thread.joinable();
} }
void Send(Direction dir, u32 message); void Send(Direction dir, u32 message);
@@ -67,6 +67,8 @@ public:
shared_memory = &shared_memory_; shared_memory = &shared_memory_;
} }
void Shutdown();
private: 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.
@@ -85,8 +87,6 @@ private:
std::jthread init_thread{}; std::jthread init_thread{};
/// Main thread /// Main thread
std::jthread main_thread{}; std::jthread main_thread{};
/// The current state
bool running{};
/// Structure shared with the host, input data set by the host before sending a mailbox message, /// Structure shared with the host, input data set by the host before sending a mailbox message,
/// and the responses are written back by the OpusDecoder. /// and the responses are written back by the OpusDecoder.
SharedMemory* shared_memory{}; SharedMemory* shared_memory{};
+1
View File
@@ -33,6 +33,7 @@ void AudioCore::CreateSinks() {
void AudioCore::Shutdown() { void AudioCore::Shutdown() {
audio_manager->Shutdown(); audio_manager->Shutdown();
adsp->Shutdown();
} }
AudioManager& AudioCore::GetAudioManager() { AudioManager& AudioCore::GetAudioManager() {
+2 -2
View File
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
const auto timed_out{events.Wait(l, std::chrono::seconds(2))}; const auto timed_out = events.Wait(l, std::chrono::seconds{2});
if (events.CheckAudioEventSet(Event::Type::Max)) { if (events.CheckAudioEventSet(Event::Type::Max)) {
break; break;
} }
@@ -35,9 +35,9 @@ AudioManager::AudioManager() {
} }
void AudioManager::Shutdown() { void AudioManager::Shutdown() {
events.SetAudioEvent(Event::Type::Max, true);
if (thread.joinable()) { if (thread.joinable()) {
thread.request_stop(); thread.request_stop();
events.SetAudioEvent(Event::Type::Max, true);
thread.join(); thread.join();
} }
} }
+1 -3
View File
@@ -39,9 +39,7 @@ class AudioManager {
public: public:
explicit AudioManager(); explicit AudioManager();
/** /// @brief Shutdown the audio manager.
* Shutdown the audio manager.
*/
void Shutdown(); void Shutdown();
/** /**
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -35,7 +38,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())}; auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())}; auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == buffer_params->in_use && !buffer_unmapped) { if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
error_info.error_code = ResultSuccess; error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0); error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2; out_status.writeOffset = current_state->last_pos2;
+19 -20
View File
@@ -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 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
}; };
struct DeviceInParameter { struct DeviceInParameter {
/* 0x000 */ char name[0x100]; /* 0x000 */ u8 name[0x100];
/* 0x100 */ u32 input_count; /* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs; /* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ char unk10A[0x1]; /* 0x10A */ u8 unk10A[0x1];
/* 0x10B */ bool downmix_enabled; /* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff; /* 0x10C */ std::array<f32, 4> downmix_coeff;
}; };
@@ -43,7 +43,7 @@ public:
struct DeviceState { struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info; /* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff; /* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ char unk18[0x18]; /* 0x18 */ u8 unk18[0x18];
}; };
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!"); static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos; /* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format; /* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs; /* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ bool in_use; /* 0x22 */ u8 in_use;
/* 0x23 */ char unk23[0x5]; /* 0x23 */ u8 unk23[0x5];
}; };
static_assert(sizeof(CircularBufferInParameter) == 0x28, static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!"); "CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2; /* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos; /* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos; /* 0x08 */ u32 last_pos;
/* 0x0C */ char unk0C[0x4]; /* 0x0C */ u8 unk0C[0x4];
/* 0x10 */ AddressInfo address_info; /* 0x10 */ AddressInfo address_info;
}; };
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!"); static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter { struct InParameter {
/* 0x000 */ Type type; /* 0x000 */ Type type;
/* 0x001 */ bool in_use; /* 0x001 */ u8 in_use;
/* 0x004 */ u32 node_id; /* 0x004 */ u32 node_id;
/* 0x008 */ char unk08[0x18]; /* 0x008 */ u8 unk08[0x18];
union { union {
/* 0x020 */ DeviceInParameter device; /* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer; /* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus { struct OutStatus {
/* 0x00 */ u32 writeOffset; /* 0x00 */ u32 writeOffset;
/* 0x04 */ char unk04[0x1C]; /* 0x04 */ u8 unk04[0x1C];
}; // size == 0x20 }; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!"); static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,19 +162,18 @@ public:
u8* GetParameter(); u8* GetParameter();
protected: protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink /// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{}; std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink /// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{};
parameter{}; /// Type of this sink
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+14 -14
View File
@@ -100,8 +100,9 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
} }
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()}, : core{core_}
adsp_rendered_event{adsp_rendered_event_} {} , adsp_rendered_event{adsp_rendered_event_}
{}
Result System::Initialize(const AudioRendererParameterInternal& params, Result System::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
@@ -406,7 +407,7 @@ void System::Finalize() {
return; return;
} }
if (active) { if (IsActive()) {
Stop(); Stop();
} }
@@ -433,14 +434,12 @@ void System::Start() {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
frames_elapsed = 0; frames_elapsed = 0;
state = State::Started; state = State::Started;
active = true;
} }
void System::Stop() { void System::Stop() {
{ {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
state = State::Stopped; state = State::Stopped;
active = false;
} }
if (execution_mode == ExecutionMode::Auto) { if (execution_mode == ExecutionMode::Auto) {
@@ -480,7 +479,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result; return result;
} }
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer, result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer,
memory_pool_count); memory_pool_count);
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_Audio, "Failed to update Effects!"); LOG_ERROR(Service_Audio, "Failed to update Effects!");
@@ -582,16 +581,16 @@ u32 System::GetRenderingDevice() const {
} }
bool System::IsActive() const { bool System::IsActive() const {
return active; return state == State::Started;
} }
void System::SendCommandToDsp() { void System::SendCommandToDsp() {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
if (initialized) { if (initialized) {
if (active) { if (IsActive()) {
terminate_event.Reset(); terminate_event.Reset();
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)}; const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id);
u64 command_size{0}; u64 command_size{0};
if (remaining_command_count) { if (remaining_command_count) {
@@ -618,16 +617,17 @@ void System::SendCommandToDsp() {
auto time_limit{ auto time_limit{
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
(static_cast<f32>(render_time_limit_percent) / 100.0f))}; (static_cast<f32>(render_time_limit_percent) / 100.0f))};
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit, core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id,
applet_resource_user_id, process_handle, translated_addr, command_size,
reset_command_buffers); time_limit, applet_resource_user_id,
process_handle, reset_command_buffers);
reset_command_buffers = false; reset_command_buffers = false;
command_buffer_size = command_size; command_buffer_size = command_size;
if (remaining_command_count == 0) { if (remaining_command_count == 0) {
adsp_rendered_event->Signal(core.Kernel()); adsp_rendered_event->Signal(core.Kernel());
} }
} else { } else {
audio_renderer.ClearRemainCommandCount(session_id); core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id);
terminate_event.Set(); terminate_event.Set();
} }
} }
@@ -738,7 +738,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
total_ticks_elapsed += end_time - start_time; total_ticks_elapsed += end_time - start_time;
num_command_lists_generated++; num_command_lists_generated++;
render_start_tick = audio_renderer.GetRenderingStartTick(session_id); render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
frames_elapsed++; frames_elapsed++;
return command_buffer.size; return command_buffer.size;
+3 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -219,8 +222,6 @@ public:
private: private:
/// Core system /// Core system
Core::System& core; Core::System& core;
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
/// Is this system initialized? /// Is this system initialized?
bool initialized{}; bool initialized{};
/// Is this system currently active? /// Is this system currently active?
+27 -30
View File
@@ -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 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -11,73 +11,70 @@
#include "audio_core/renderer/system_manager.h" #include "audio_core/renderer/system_manager.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/core_timing.h" #include "core/core_timing.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
SystemManager::SystemManager(Core::System& core_) SystemManager::SystemManager(Core::System& core_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {} : core{core_}
{
}
SystemManager::~SystemManager() { SystemManager::~SystemManager() {
Stop(); Stop();
} }
void SystemManager::InitializeUnsafe() { void SystemManager::InitializeUnsafe() {
if (!active) { if (!thread.joinable()) {
active = true; core.AudioCore().ADSP().AudioRenderer().Start();
audio_renderer.Start(); thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() {
thread = std::jthread([this](std::stop_token stop_token) { auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
Common::SetCurrentThreadName("AudioRenderSystemManager"); auto const stop_token = thread.get_stop_token();
Common::SetCurrentThreadPriority(Common::ThreadPriority::High); while (!stop_token.stop_requested()) {
while (active && !stop_token.stop_requested()) {
{ {
std::scoped_lock l{mutex1}; std::scoped_lock lk{mutex};
for (auto system : systems) for (auto system : systems)
system->SendCommandToDsp(); system->SendCommandToDsp();
} }
audio_renderer.Signal(); audio_renderer.Signal();
audio_renderer.Wait(); audio_renderer.Wait(stop_token);
} }
}); });
} }
} }
void SystemManager::Stop() { void SystemManager::Stop() {
if (active) { core.AudioCore().ADSP().AudioRenderer().Stop();
active = false; if (thread.joinable()) {
thread.request_stop(); thread.request_stop();
thread.join(); thread.join();
audio_renderer.Stop();
} }
} }
bool SystemManager::Add(System& system_) { bool SystemManager::Add(System& system_) {
std::scoped_lock l2{mutex2}; std::scoped_lock lk{mutex};
if (systems.size() + 1 > MaxRendererSessions) { if (systems.size() >= MaxRendererSessions) {
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!"); LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
return false; return false;
} }
{
std::scoped_lock l{mutex1};
if (systems.empty())
InitializeUnsafe();
}
systems.push_back(&system_); systems.push_back(&system_);
if (!systems.empty()) {
InitializeUnsafe();
}
return true; return true;
} }
bool SystemManager::Remove(System& system_) { bool SystemManager::Remove(System& system_) {
std::scoped_lock l2{mutex2}; std::scoped_lock lk{mutex};
{ if (systems.remove(&system_) == 0) {
std::scoped_lock l{mutex1}; LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
if (systems.remove(&system_) == 0) { return false;
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false;
}
} }
if (systems.empty()) {
if (systems.empty())
Stop(); Stop();
}
return true; return true;
} }
+3 -9
View File
@@ -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 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -75,14 +75,8 @@ private:
std::list<System*> systems{}; std::list<System*> systems{};
/// Main worker thread for generating command lists /// Main worker thread for generating command lists
std::jthread thread; std::jthread thread;
/// Mutex for the systems /// Mutex for the reading and adding/removing systems
std::mutex mutex1{}; std::mutex mutex{};
/// Mutex for adding/removing systems
std::mutex mutex2{};
/// Is the system manager thread active?
std::atomic<bool> active{};
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+1
View File
@@ -62,6 +62,7 @@ add_library(
fs/fs_util.h fs/fs_util.h
fs/path_util.cpp fs/path_util.cpp
fs/path_util.h fs/path_util.h
hash.h
heap_tracker.cpp heap_tracker.cpp
heap_tracker.h heap_tracker.h
hex_util.cpp hex_util.cpp
+251 -252
View File
@@ -12,94 +12,93 @@
#include "common/android/multiplayer/multiplayer.h" #include "common/android/multiplayer/multiplayer.h"
#include <network/network.h> #include <network/network.h>
static struct {
JavaVM *java_vm;
jclass native_library_class;
jclass disk_cache_progress_class;
jclass load_callback_stage_class;
jclass game_dir_class;
jmethodID game_dir_constructor;
jmethodID exit_emulation_activity;
jmethodID disk_cache_load_progress;
jmethodID on_emulation_started;
jmethodID on_emulation_stopped;
jmethodID on_program_changed;
jmethodID copy_to_storage;
jmethodID file_exists;
jmethodID file_extension;
jclass game_class; static JavaVM *s_java_vm;
jmethodID game_constructor; static jclass s_native_library_class;
jfieldID game_title_field; static jclass s_disk_cache_progress_class;
jfieldID game_path_field; static jclass s_load_callback_stage_class;
jfieldID game_program_id_field; static jclass s_game_dir_class;
jfieldID game_developer_field; static jmethodID s_game_dir_constructor;
jfieldID game_version_field; static jmethodID s_exit_emulation_activity;
jfieldID game_is_homebrew_field; static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started;
static jmethodID s_on_emulation_stopped;
static jmethodID s_on_program_changed;
static jmethodID s_copy_to_storage;
static jmethodID s_file_exists;
static jmethodID s_file_extension;
jclass string_class; static jclass s_game_class;
jclass pair_class; static jmethodID s_game_constructor;
jmethodID pair_constructor; static jfieldID s_game_title_field;
jfieldID pair_first_field; static jfieldID s_game_path_field;
jfieldID pair_second_field; static jfieldID s_game_program_id_field;
static jfieldID s_game_developer_field;
static jfieldID s_game_version_field;
static jfieldID s_game_is_homebrew_field;
jclass overlay_control_data_class; static jclass s_string_class;
jmethodID overlay_control_data_constructor; static jclass s_pair_class;
jfieldID overlay_control_data_id_field; static jmethodID s_pair_constructor;
jfieldID overlay_control_data_enabled_field; static jfieldID s_pair_first_field;
jfieldID overlay_control_data_individual_scale_field; static jfieldID s_pair_second_field;
jfieldID overlay_control_data_landscape_position_field;
jfieldID overlay_control_data_portrait_position_field;
jfieldID overlay_control_data_foldable_position_field;
jclass patch_class; static jclass s_overlay_control_data_class;
jmethodID patch_constructor; static jmethodID s_overlay_control_data_constructor;
jfieldID patch_enabled_field; static jfieldID s_overlay_control_data_id_field;
jfieldID patch_name_field; static jfieldID s_overlay_control_data_enabled_field;
jfieldID patch_version_field; static jfieldID s_overlay_control_data_individual_scale_field;
jfieldID patch_type_field; static jfieldID s_overlay_control_data_landscape_position_field;
jfieldID patch_program_id_field; static jfieldID s_overlay_control_data_portrait_position_field;
jfieldID patch_title_id_field; static jfieldID s_overlay_control_data_foldable_position_field;
jclass double_class; static jclass s_patch_class;
jmethodID double_constructor; static jmethodID s_patch_constructor;
jmethodID double_value_method; static jfieldID s_patch_enabled_field;
static jfieldID s_patch_name_field;
static jfieldID s_patch_version_field;
static jfieldID s_patch_type_field;
static jfieldID s_patch_program_id_field;
static jfieldID s_patch_title_id_field;
jclass integer_class; static jclass s_double_class;
jmethodID integer_constructor; static jmethodID s_double_constructor;
jmethodID integer_value_method; static jmethodID s_double_value_method;
jclass boolean_class; static jclass s_integer_class;
jmethodID boolean_constructor; static jmethodID s_integer_constructor;
jmethodID boolean_value_method; static jmethodID s_integer_value_method;
jclass player_input_class; static jclass s_boolean_class;
jmethodID player_input_constructor; static jmethodID s_boolean_constructor;
jfieldID player_input_connected_field; static jmethodID s_boolean_value_method;
jfieldID player_input_buttons_field;
jfieldID player_input_analogs_field;
jfieldID player_input_motions_field;
jfieldID player_input_vibration_enabled_field;
jfieldID player_input_vibration_strength_field;
jfieldID player_input_body_color_left_field;
jfieldID player_input_body_color_right_field;
jfieldID player_input_button_color_left_field;
jfieldID player_input_button_color_right_field;
jfieldID player_input_profile_name_field;
jfieldID player_input_use_system_vibrator_field;
jclass yuzu_input_device_interface; static jclass s_player_input_class;
jmethodID yuzu_input_device_get_name; static jmethodID s_player_input_constructor;
jmethodID yuzu_input_device_get_guid; static jfieldID s_player_input_connected_field;
jmethodID yuzu_input_device_get_port; static jfieldID s_player_input_buttons_field;
jmethodID yuzu_input_device_get_supports_vibration; static jfieldID s_player_input_analogs_field;
jmethodID yuzu_input_device_vibrate; static jfieldID s_player_input_motions_field;
jmethodID yuzu_input_device_get_axes; static jfieldID s_player_input_vibration_enabled_field;
jmethodID yuzu_input_device_has_keys; static jfieldID s_player_input_vibration_strength_field;
static jfieldID s_player_input_body_color_left_field;
static jfieldID s_player_input_body_color_right_field;
static jfieldID s_player_input_button_color_left_field;
static jfieldID s_player_input_button_color_right_field;
static jfieldID s_player_input_profile_name_field;
static jfieldID s_player_input_use_system_vibrator_field;
jmethodID add_netplay_message; static jclass s_yuzu_input_device_interface;
jmethodID clear_chat; static jmethodID s_yuzu_input_device_get_name;
} state; static jmethodID s_yuzu_input_device_get_guid;
static jmethodID s_yuzu_input_device_get_port;
static jmethodID s_yuzu_input_device_get_supports_vibration;
static jmethodID s_yuzu_input_device_vibrate;
static jmethodID s_yuzu_input_device_get_axes;
static jmethodID s_yuzu_input_device_has_keys;
static jmethodID s_add_netplay_message;
static jmethodID s_clear_chat;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
@@ -107,14 +106,14 @@ namespace Common::Android {
JNIEnv *GetEnvForThread() { JNIEnv *GetEnvForThread() {
thread_local static struct OwnedEnv { thread_local static struct OwnedEnv {
OwnedEnv() { OwnedEnv() {
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6); status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED) if (status == JNI_EDETACHED)
state.java_vm->AttachCurrentThread(&env, nullptr); s_java_vm->AttachCurrentThread(&env, nullptr);
} }
~OwnedEnv() { ~OwnedEnv() {
if (status == JNI_EDETACHED) if (status == JNI_EDETACHED)
state.java_vm->DetachCurrentThread(); s_java_vm->DetachCurrentThread();
} }
int status; int status;
@@ -124,303 +123,303 @@ namespace Common::Android {
} }
jclass GetNativeLibraryClass() { jclass GetNativeLibraryClass() {
return state.native_library_class; return s_native_library_class;
} }
jclass GetDiskCacheProgressClass() { jclass GetDiskCacheProgressClass() {
return state.disk_cache_progress_class; return s_disk_cache_progress_class;
} }
jclass GetDiskCacheLoadCallbackStageClass() { jclass GetDiskCacheLoadCallbackStageClass() {
return state.load_callback_stage_class; return s_load_callback_stage_class;
} }
jclass GetGameDirClass() { jclass GetGameDirClass() {
return state.game_dir_class; return s_game_dir_class;
} }
jmethodID GetGameDirConstructor() { jmethodID GetGameDirConstructor() {
return state.game_dir_constructor; return s_game_dir_constructor;
} }
jmethodID GetExitEmulationActivity() { jmethodID GetExitEmulationActivity() {
return state.exit_emulation_activity; return s_exit_emulation_activity;
} }
jmethodID GetDiskCacheLoadProgress() { jmethodID GetDiskCacheLoadProgress() {
return state.disk_cache_load_progress; return s_disk_cache_load_progress;
} }
jmethodID GetCopyToStorage() { jmethodID GetCopyToStorage() {
return state.copy_to_storage; return s_copy_to_storage;
} }
jmethodID GetFileExists() { jmethodID GetFileExists() {
return state.file_exists; return s_file_exists;
} }
jmethodID GetFileExtension() { jmethodID GetFileExtension() {
return state.file_extension; return s_file_extension;
} }
jmethodID GetOnEmulationStarted() { jmethodID GetOnEmulationStarted() {
return state.on_emulation_started; return s_on_emulation_started;
} }
jmethodID GetOnEmulationStopped() { jmethodID GetOnEmulationStopped() {
return state.on_emulation_stopped; return s_on_emulation_stopped;
} }
jmethodID GetOnProgramChanged() { jmethodID GetOnProgramChanged() {
return state.on_program_changed; return s_on_program_changed;
} }
jclass GetGameClass() { jclass GetGameClass() {
return state.game_class; return s_game_class;
} }
jmethodID GetGameConstructor() { jmethodID GetGameConstructor() {
return state.game_constructor; return s_game_constructor;
} }
jfieldID GetGameTitleField() { jfieldID GetGameTitleField() {
return state.game_title_field; return s_game_title_field;
} }
jfieldID GetGamePathField() { jfieldID GetGamePathField() {
return state.game_path_field; return s_game_path_field;
} }
jfieldID GetGameProgramIdField() { jfieldID GetGameProgramIdField() {
return state.game_program_id_field; return s_game_program_id_field;
} }
jfieldID GetGameDeveloperField() { jfieldID GetGameDeveloperField() {
return state.game_developer_field; return s_game_developer_field;
} }
jfieldID GetGameVersionField() { jfieldID GetGameVersionField() {
return state.game_version_field; return s_game_version_field;
} }
jfieldID GetGameIsHomebrewField() { jfieldID GetGameIsHomebrewField() {
return state.game_is_homebrew_field; return s_game_is_homebrew_field;
} }
jclass GetStringClass() { jclass GetStringClass() {
return state.string_class; return s_string_class;
} }
jclass GetPairClass() { jclass GetPairClass() {
return state.pair_class; return s_pair_class;
} }
jmethodID GetPairConstructor() { jmethodID GetPairConstructor() {
return state.pair_constructor; return s_pair_constructor;
} }
jfieldID GetPairFirstField() { jfieldID GetPairFirstField() {
return state.pair_first_field; return s_pair_first_field;
} }
jfieldID GetPairSecondField() { jfieldID GetPairSecondField() {
return state.pair_second_field; return s_pair_second_field;
} }
jclass GetOverlayControlDataClass() { jclass GetOverlayControlDataClass() {
return state.overlay_control_data_class; return s_overlay_control_data_class;
} }
jmethodID GetOverlayControlDataConstructor() { jmethodID GetOverlayControlDataConstructor() {
return state.overlay_control_data_constructor; return s_overlay_control_data_constructor;
} }
jfieldID GetOverlayControlDataIdField() { jfieldID GetOverlayControlDataIdField() {
return state.overlay_control_data_id_field; return s_overlay_control_data_id_field;
} }
jfieldID GetOverlayControlDataEnabledField() { jfieldID GetOverlayControlDataEnabledField() {
return state.overlay_control_data_enabled_field; return s_overlay_control_data_enabled_field;
} }
jfieldID GetOverlayControlDataIndividualScaleField() { jfieldID GetOverlayControlDataIndividualScaleField() {
return state.overlay_control_data_individual_scale_field; return s_overlay_control_data_individual_scale_field;
} }
jfieldID GetOverlayControlDataLandscapePositionField() { jfieldID GetOverlayControlDataLandscapePositionField() {
return state.overlay_control_data_landscape_position_field; return s_overlay_control_data_landscape_position_field;
} }
jfieldID GetOverlayControlDataPortraitPositionField() { jfieldID GetOverlayControlDataPortraitPositionField() {
return state.overlay_control_data_portrait_position_field; return s_overlay_control_data_portrait_position_field;
} }
jfieldID GetOverlayControlDataFoldablePositionField() { jfieldID GetOverlayControlDataFoldablePositionField() {
return state.overlay_control_data_foldable_position_field; return s_overlay_control_data_foldable_position_field;
} }
jclass GetPatchClass() { jclass GetPatchClass() {
return state.patch_class; return s_patch_class;
} }
jmethodID GetPatchConstructor() { jmethodID GetPatchConstructor() {
return state.patch_constructor; return s_patch_constructor;
} }
jfieldID GetPatchEnabledField() { jfieldID GetPatchEnabledField() {
return state.patch_enabled_field; return s_patch_enabled_field;
} }
jfieldID GetPatchNameField() { jfieldID GetPatchNameField() {
return state.patch_name_field; return s_patch_name_field;
} }
jfieldID GetPatchVersionField() { jfieldID GetPatchVersionField() {
return state.patch_version_field; return s_patch_version_field;
} }
jfieldID GetPatchTypeField() { jfieldID GetPatchTypeField() {
return state.patch_type_field; return s_patch_type_field;
} }
jfieldID GetPatchProgramIdField() { jfieldID GetPatchProgramIdField() {
return state.patch_program_id_field; return s_patch_program_id_field;
} }
jfieldID GetPatchTitleIdField() { jfieldID GetPatchTitleIdField() {
return state.patch_title_id_field; return s_patch_title_id_field;
} }
jclass GetDoubleClass() { jclass GetDoubleClass() {
return state.double_class; return s_double_class;
} }
jmethodID GetDoubleConstructor() { jmethodID GetDoubleConstructor() {
return state.double_constructor; return s_double_constructor;
} }
jmethodID GetDoubleValueMethod() { jmethodID GetDoubleValueMethod() {
return state.double_value_method; return s_double_value_method;
} }
jclass GetIntegerClass() { jclass GetIntegerClass() {
return state.integer_class; return s_integer_class;
} }
jmethodID GetIntegerConstructor() { jmethodID GetIntegerConstructor() {
return state.integer_constructor; return s_integer_constructor;
} }
jmethodID GetIntegerValueMethod() { jmethodID GetIntegerValueMethod() {
return state.integer_value_method; return s_integer_value_method;
} }
jclass GetBooleanClass() { jclass GetBooleanClass() {
return state.boolean_class; return s_boolean_class;
} }
jmethodID GetBooleanConstructor() { jmethodID GetBooleanConstructor() {
return state.boolean_constructor; return s_boolean_constructor;
} }
jmethodID GetBooleanValueMethod() { jmethodID GetBooleanValueMethod() {
return state.boolean_value_method; return s_boolean_value_method;
} }
jclass GetPlayerInputClass() { jclass GetPlayerInputClass() {
return state.player_input_class; return s_player_input_class;
} }
jmethodID GetPlayerInputConstructor() { jmethodID GetPlayerInputConstructor() {
return state.player_input_constructor; return s_player_input_constructor;
} }
jfieldID GetPlayerInputConnectedField() { jfieldID GetPlayerInputConnectedField() {
return state.player_input_connected_field; return s_player_input_connected_field;
} }
jfieldID GetPlayerInputButtonsField() { jfieldID GetPlayerInputButtonsField() {
return state.player_input_buttons_field; return s_player_input_buttons_field;
} }
jfieldID GetPlayerInputAnalogsField() { jfieldID GetPlayerInputAnalogsField() {
return state.player_input_analogs_field; return s_player_input_analogs_field;
} }
jfieldID GetPlayerInputMotionsField() { jfieldID GetPlayerInputMotionsField() {
return state.player_input_motions_field; return s_player_input_motions_field;
} }
jfieldID GetPlayerInputVibrationEnabledField() { jfieldID GetPlayerInputVibrationEnabledField() {
return state.player_input_vibration_enabled_field; return s_player_input_vibration_enabled_field;
} }
jfieldID GetPlayerInputVibrationStrengthField() { jfieldID GetPlayerInputVibrationStrengthField() {
return state.player_input_vibration_strength_field; return s_player_input_vibration_strength_field;
} }
jfieldID GetPlayerInputBodyColorLeftField() { jfieldID GetPlayerInputBodyColorLeftField() {
return state.player_input_body_color_left_field; return s_player_input_body_color_left_field;
} }
jfieldID GetPlayerInputBodyColorRightField() { jfieldID GetPlayerInputBodyColorRightField() {
return state.player_input_body_color_right_field; return s_player_input_body_color_right_field;
} }
jfieldID GetPlayerInputButtonColorLeftField() { jfieldID GetPlayerInputButtonColorLeftField() {
return state.player_input_button_color_left_field; return s_player_input_button_color_left_field;
} }
jfieldID GetPlayerInputButtonColorRightField() { jfieldID GetPlayerInputButtonColorRightField() {
return state.player_input_button_color_right_field; return s_player_input_button_color_right_field;
} }
jfieldID GetPlayerInputProfileNameField() { jfieldID GetPlayerInputProfileNameField() {
return state.player_input_profile_name_field; return s_player_input_profile_name_field;
} }
jfieldID GetPlayerInputUseSystemVibratorField() { jfieldID GetPlayerInputUseSystemVibratorField() {
return state.player_input_use_system_vibrator_field; return s_player_input_use_system_vibrator_field;
} }
jclass GetYuzuInputDeviceInterface() { jclass GetYuzuInputDeviceInterface() {
return state.yuzu_input_device_interface; return s_yuzu_input_device_interface;
} }
jmethodID GetYuzuDeviceGetName() { jmethodID GetYuzuDeviceGetName() {
return state.yuzu_input_device_get_name; return s_yuzu_input_device_get_name;
} }
jmethodID GetYuzuDeviceGetGUID() { jmethodID GetYuzuDeviceGetGUID() {
return state.yuzu_input_device_get_guid; return s_yuzu_input_device_get_guid;
} }
jmethodID GetYuzuDeviceGetPort() { jmethodID GetYuzuDeviceGetPort() {
return state.yuzu_input_device_get_port; return s_yuzu_input_device_get_port;
} }
jmethodID GetYuzuDeviceGetSupportsVibration() { jmethodID GetYuzuDeviceGetSupportsVibration() {
return state.yuzu_input_device_get_supports_vibration; return s_yuzu_input_device_get_supports_vibration;
} }
jmethodID GetYuzuDeviceVibrate() { jmethodID GetYuzuDeviceVibrate() {
return state.yuzu_input_device_vibrate; return s_yuzu_input_device_vibrate;
} }
jmethodID GetYuzuDeviceGetAxes() { jmethodID GetYuzuDeviceGetAxes() {
return state.yuzu_input_device_get_axes; return s_yuzu_input_device_get_axes;
} }
jmethodID GetYuzuDeviceHasKeys() { jmethodID GetYuzuDeviceHasKeys() {
return state.yuzu_input_device_has_keys; return s_yuzu_input_device_has_keys;
} }
jmethodID GetAddNetPlayMessage() { jmethodID GetAddNetPlayMessage() {
return state.add_netplay_message; return s_add_netplay_message;
} }
jmethodID ClearChat() { jmethodID ClearChat() {
return state.clear_chat; return s_clear_chat;
} }
#ifdef __cplusplus #ifdef __cplusplus
@@ -437,20 +436,20 @@ namespace Common::Android {
// UnInitialize Android Storage // UnInitialize Android Storage
Common::FS::Android::UnRegisterCallbacks(); Common::FS::Android::UnRegisterCallbacks();
env->DeleteGlobalRef(state.native_library_class); env->DeleteGlobalRef(s_native_library_class);
env->DeleteGlobalRef(state.disk_cache_progress_class); env->DeleteGlobalRef(s_disk_cache_progress_class);
env->DeleteGlobalRef(state.load_callback_stage_class); env->DeleteGlobalRef(s_load_callback_stage_class);
env->DeleteGlobalRef(state.game_dir_class); env->DeleteGlobalRef(s_game_dir_class);
env->DeleteGlobalRef(state.game_class); env->DeleteGlobalRef(s_game_class);
env->DeleteGlobalRef(state.string_class); env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(state.pair_class); env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(state.overlay_control_data_class); env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(state.patch_class); env->DeleteGlobalRef(s_patch_class);
env->DeleteGlobalRef(state.double_class); env->DeleteGlobalRef(s_double_class);
env->DeleteGlobalRef(state.integer_class); env->DeleteGlobalRef(s_integer_class);
env->DeleteGlobalRef(state.boolean_class); env->DeleteGlobalRef(s_boolean_class);
env->DeleteGlobalRef(state.player_input_class); env->DeleteGlobalRef(s_player_input_class);
env->DeleteGlobalRef(state.yuzu_input_device_interface); env->DeleteGlobalRef(s_yuzu_input_device_interface);
// UnInitialize applets // UnInitialize applets
SoftwareKeyboard::CleanupJNI(env); SoftwareKeyboard::CleanupJNI(env);
@@ -464,7 +463,7 @@ namespace Common::Android {
#endif #endif
void Initialize(JavaVM* vm, JNIEnv *env) { void Initialize(JavaVM* vm, JNIEnv *env) {
state.java_vm = vm; s_java_vm = vm;
InitFFmpegOnLoad(vm); InitFFmpegOnLoad(vm);
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
@@ -473,169 +472,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
// Initialize Java classes // Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary"); const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class)); s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef( s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress"))); env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass( s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir"); const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class)); s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V"); "(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class); env->DeleteLocalRef(game_dir_class);
// Initialize methods // Initialize methods
state.exit_emulation_activity = s_exit_emulation_activity =
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V"); env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
state.disk_cache_load_progress = s_disk_cache_load_progress =
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V"); env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage", s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z"); "(Ljava/lang/String;Ljava/lang/String;)Z");
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists", s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z"); "(Ljava/lang/String;)Z");
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension", s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;"); "(Ljava/lang/String;)Ljava/lang/String;");
state.on_emulation_started = s_on_emulation_started =
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V"); env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
state.on_emulation_stopped = s_on_emulation_stopped =
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V"); env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
state.on_program_changed = s_on_program_changed =
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V"); env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
state.game_constructor = env->GetMethodID(game_class, "<init>", s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/" "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V"); "String;Ljava/lang/String;Ljava/lang/String;Z)V");
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;"); s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;"); s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;"); s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;"); s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;"); s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z"); s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class); env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String"); const jclass string_class = env->FindClass("java/lang/String");
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class)); s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class); env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair"); const jclass pair_class = env->FindClass("kotlin/Pair");
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class)); s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
state.pair_constructor = s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V"); env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;"); s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class); env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class = const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData"); env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
state.overlay_control_data_class = s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class)); reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
state.overlay_control_data_constructor = s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>", env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V"); "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
state.overlay_control_data_id_field = s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;"); env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
state.overlay_control_data_enabled_field = s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z"); env->GetFieldID(overlay_control_data_class, "enabled", "Z");
state.overlay_control_data_landscape_position_field = s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;"); env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
state.overlay_control_data_portrait_position_field = s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;"); env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
state.overlay_control_data_foldable_position_field = s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;"); env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
state.overlay_control_data_individual_scale_field = s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F"); env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class); env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch"); const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class)); s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
state.patch_constructor = env->GetMethodID( s_patch_constructor = env->GetMethodID(
patch_class, "<init>", patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V"); "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
state.patch_type_field = env->GetFieldID(patch_class, "type", "I"); s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;"); s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class); env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double"); const jclass double_class = env->FindClass("java/lang/Double");
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class)); s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V"); s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D"); s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
env->DeleteLocalRef(double_class); env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer"); const jclass int_class = env->FindClass("java/lang/Integer");
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class)); s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V"); s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I"); s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
env->DeleteLocalRef(int_class); env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean"); const jclass boolean_class = env->FindClass("java/lang/Boolean");
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class)); s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V"); s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z"); s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
env->DeleteLocalRef(boolean_class); env->DeleteLocalRef(boolean_class);
const jclass player_input_class = const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput"); env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class)); s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
state.player_input_constructor = env->GetMethodID( s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>", player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V"); "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z"); s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
state.player_input_buttons_field = s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;"); env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
state.player_input_analogs_field = s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;"); env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
state.player_input_motions_field = s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;"); env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
state.player_input_vibration_enabled_field = s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z"); env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
state.player_input_vibration_strength_field = s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I"); env->GetFieldID(player_input_class, "vibrationStrength", "I");
state.player_input_body_color_left_field = s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J"); env->GetFieldID(player_input_class, "bodyColorLeft", "J");
state.player_input_body_color_right_field = s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J"); env->GetFieldID(player_input_class, "bodyColorRight", "J");
state.player_input_button_color_left_field = s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J"); env->GetFieldID(player_input_class, "buttonColorLeft", "J");
state.player_input_button_color_right_field = s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J"); env->GetFieldID(player_input_class, "buttonColorRight", "J");
state.player_input_profile_name_field = s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;"); env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
state.player_input_use_system_vibrator_field = s_player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z"); env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class); env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface = const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice"); env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
state.yuzu_input_device_interface = s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface)); reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
state.yuzu_input_device_get_name = s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;"); env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
state.yuzu_input_device_get_guid = s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;"); env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I"); "()I");
state.yuzu_input_device_get_supports_vibration = s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z"); env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V"); "(F)V");
state.yuzu_input_device_get_axes = s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;"); env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
state.yuzu_input_device_has_keys = s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z"); env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface); env->DeleteLocalRef(yuzu_input_device_interface);
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage", s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V"); "(ILjava/lang/String;)V");
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V"); s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage // Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, state.native_library_class); Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets // Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env); Common::Android::SoftwareKeyboard::InitJNI(env);
+18 -4
View File
@@ -22,22 +22,36 @@ template <typename T>
return std::size_t(sizeof(T) * CHAR_BIT); return std::size_t(sizeof(T) * CHAR_BIT);
} }
template<typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
}
template<typename T> template<typename T>
requires std::is_integral_v<T> requires std::is_integral_v<T>
[[nodiscard]] constexpr T Log2Floor(const T value) { [[nodiscard]] constexpr T Log2Floor(const T value) {
return std::bit_width(value) - 1; return T(MostSignificantBit<T>(value));
} }
template<typename T> template<typename T>
requires std::is_integral_v<T> requires std::is_integral_v<T>
[[nodiscard]] constexpr T Log2Ceil(const T value) { [[nodiscard]] constexpr T Log2Ceil(const T value) {
return std::bit_width(value - 1); const T log2_f = Log2Floor<T>(value);
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] T NextPow2(T value) {
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
} }
template <size_t bit_index, typename T> template <size_t bit_index, typename T>
requires (std::is_integral_v<T> && bit_index < BitSize<T>()) requires std::is_integral_v<T>
[[nodiscard]] constexpr bool Bit(const T value) { [[nodiscard]] constexpr bool Bit(const T value) {
return (T(value >> bit_index) & T(1)) == T(1); static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
return ((value >> bit_index) & T(1)) == T(1);
} }
} // namespace Common } // namespace Common
+7 -7
View File
@@ -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
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -109,12 +109,6 @@ private:
if (read_index == producer.index.load(std::memory_order::acquire)) { if (read_index == producer.index.load(std::memory_order::acquire)) {
return false; return false;
} }
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else if constexpr (Mode == PopMode::WaitWithStopToken) { } else if constexpr (Mode == PopMode::WaitWithStopToken) {
// Wait until the queue is not empty. // Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex}; std::unique_lock lock{consumer.cv_mutex};
@@ -124,6 +118,12 @@ private:
if (stop_token.stop_requested()) { if (stop_token.stop_requested()) {
return false; return false;
} }
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else { } else {
static_assert(Mode < PopMode::Count, "Invalid PopMode."); static_assert(Mode < PopMode::Count, "Invalid PopMode.");
} }
+13 -5
View File
@@ -25,18 +25,26 @@ template <typename T>
requires std::is_unsigned_v<T> requires std::is_unsigned_v<T>
inline std::size_t HashValue(T val) { inline std::size_t HashValue(T val) {
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits; const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits); const unsigned int length =
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
std::size_t seed = 0; std::size_t seed = 0;
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2); for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2); seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
}
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
return seed;
} }
template <size_t Bits> template <size_t Bits>
struct HashCombineImpl { struct HashCombineImpl {
template <typename T> template <typename T>
static inline T fn(T seed, T value) { static inline T fn(T seed, T value) {
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
} }
}; };
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,14 +12,14 @@ namespace Common {
template <typename N, typename D> template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D> requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) { [[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return N((D(number) + divisor - 1) / divisor); return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
} }
/// Ceiled integer division with logarithmic divisor in base 2 /// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D> template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D> requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { [[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
} }
} // namespace Common } // namespace Common
+28
View File
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <utility>
#include <boost/functional/hash.hpp>
namespace Common {
struct PairHash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
std::size_t seed = std::hash<T1>()(pair.first);
boost::hash_combine(seed, std::hash<T2>()(pair.second));
return seed;
}
};
template <typename T>
struct IdentityHash {
[[nodiscard]] size_t operator()(T value) const noexcept {
return static_cast<size_t>(value);
}
};
} // namespace Common
+16 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -19,12 +16,14 @@ namespace Common {
[[nodiscard]] constexpr u8 ToHexNibble(char c) { [[nodiscard]] constexpr u8 ToHexNibble(char c) {
if (c >= 65 && c <= 70) { if (c >= 65 && c <= 70) {
return u8(c - 55); return static_cast<u8>(c - 55);
} }
if (c >= 97 && c <= 102) { if (c >= 97 && c <= 102) {
return u8(c - 87); return static_cast<u8>(c - 87);
} }
return u8(c - 48);
return static_cast<u8>(c - 48);
} }
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); [[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
@@ -32,28 +31,35 @@ namespace Common {
template <std::size_t Size, bool le = false> template <std::size_t Size, bool le = false>
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { [[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size"); ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
std::array<u8, Size> out{}; std::array<u8, Size> out{};
if constexpr (le) { if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1])); out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
} }
} else { } else {
for (std::size_t i = 0; i < 2 * Size; i += 2) { for (std::size_t i = 0; i < 2 * Size; i += 2) {
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1])); out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
} }
} }
return out; return out;
} }
template <typename ContiguousContainer> template <typename ContiguousContainer>
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) { [[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
"Underlying type within the contiguous container must be u8.");
constexpr std::size_t pad_width = 2; constexpr std::size_t pad_width = 2;
std::string out; std::string out;
out.reserve(std::size(data) * pad_width); out.reserve(std::size(data) * pad_width);
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}"); const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
for (const u8 c : data) for (const u8 c : data) {
out += fmt::format(format_str, c); out += fmt::format(format_str, c);
}
return out; return out;
} }
+6 -10
View File
@@ -10,7 +10,6 @@
#include <cstdlib> #include <cstdlib>
#include <regex> #include <regex>
#include <thread> #include <thread>
#include <boost/algorithm/string/predicate.hpp>
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <android/log.h> #include <android/log.h>
@@ -96,10 +95,9 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
template <typename It> template <typename It>
Level GetLevelByName(const It begin, const It end) { Level GetLevelByName(const It begin, const It end) {
std::string_view const sv{begin, end};
for (u32 i = 0; i < u32(Level::Count); ++i) { for (u32 i = 0; i < u32(Level::Count); ++i) {
auto const level_name = GetLevelName(Level(i)); const char* level_name = GetLevelName(Level(i));
if (boost::iequals(sv, level_name)) if (Common::ComparePartialString(begin, end, level_name))
return Level(i); return Level(i);
} }
return Level::Count; return Level::Count;
@@ -107,10 +105,9 @@ Level GetLevelByName(const It begin, const It end) {
template <typename It> template <typename It>
Class GetClassByName(const It begin, const It end) { Class GetClassByName(const It begin, const It end) {
std::string_view const sv{begin, end};
for (u32 i = 0; i < u32(Class::Count); ++i) { for (u32 i = 0; i < u32(Class::Count); ++i) {
auto const level_name = GetLogClassName(Class(i)); const char* level_name = GetLogClassName(Class(i));
if (boost::iequals(sv, level_name)) if (Common::ComparePartialString(begin, end, level_name))
return Class(i); return Class(i);
} }
return Class::Count; return Class::Count;
@@ -123,13 +120,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end)); LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
return false; return false;
} }
auto const sv = std::string_view{begin, level_separator}; const Level level = GetLevelByName(level_separator + 1, end);
auto const level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) { if (level == Level::Count) {
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end)); LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
return false; return false;
} }
if (boost::iequals(sv, "*")) { if (Common::ComparePartialString(begin, level_separator, "*")) {
instance.class_levels.fill(level); instance.class_levels.fill(level);
return true; return true;
} }
+18 -20
View File
@@ -8,9 +8,6 @@
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include "common/logging.h" #include "common/logging.h"
#include "common/param_package.h" #include "common/param_package.h"
@@ -18,16 +15,17 @@
namespace Common { namespace Common {
constexpr auto KEY_VALUE_SEPARATOR = ":"; constexpr char KEY_VALUE_SEPARATOR = ':';
constexpr auto PARAM_SEPARATOR = ","; constexpr char PARAM_SEPARATOR = ',';
constexpr auto ESCAPE_CHARACTER = "$";
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0"; constexpr char ESCAPE_CHARACTER = '$';
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1"; constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2"; constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
/// A placeholder for empty param packages to avoid empty strings /// A placeholder for empty param packages to avoid empty strings
/// (they may be recognized as "not set" by some frontend libraries like qt) /// (they may be recognized as "not set" by some frontend libraries like qt)
constexpr auto EMPTY_PLACEHOLDER = "[empty]"; constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
ParamPackage::ParamPackage(const std::string& serialized) { ParamPackage::ParamPackage(const std::string& serialized) {
if (serialized == EMPTY_PLACEHOLDER) { if (serialized == EMPTY_PLACEHOLDER) {
@@ -35,20 +33,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
} }
std::vector<std::string> pairs; std::vector<std::string> pairs;
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR)); Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
for (const std::string& pair : pairs) { for (const std::string& pair : pairs) {
std::vector<std::string> key_value; std::vector<std::string> key_value;
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR)); Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
if (key_value.size() != 2) { if (key_value.size() != 2) {
LOG_ERROR(Common, "invalid key pair {}", pair); LOG_ERROR(Common, "invalid key pair {}", pair);
continue; continue;
} }
for (auto& part : key_value) { for (std::string& part : key_value) {
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR); part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR); part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER); part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
} }
Set(key_value[0], std::move(key_value[1])); Set(key_value[0], std::move(key_value[1]));
@@ -65,10 +63,10 @@ std::string ParamPackage::Serialize() const {
for (const auto& pair : data) { for (const auto& pair : data) {
std::array<std::string, 2> key_value{{pair.first, pair.second}}; std::array<std::string, 2> key_value{{pair.first, pair.second}};
for (auto& part : key_value) { for (std::string& part : key_value) {
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE); part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE); part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE); part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
} }
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR; result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
} }
+51
View File
@@ -24,6 +24,22 @@
namespace Common { namespace Common {
/// Make a string lowercase
std::string ToLower(const std::string_view sv) {
std::string str{sv};
std::transform(str.begin(), str.end(), str.begin(),
[](auto const c) { return char(std::tolower(c)); });
return str;
}
/// Make a string uppercase
std::string ToUpper(const std::string_view sv) {
std::string str{sv};
std::transform(str.begin(), str.end(), str.begin(),
[](auto const c) { return char(std::toupper(c)); });
return str;
}
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension) { std::string* _pExtension) {
if (full_path.empty()) if (full_path.empty())
@@ -64,6 +80,41 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
return true; return true;
} }
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim)) {
output.emplace_back();
}
output.pop_back();
}
std::string TabsToSpaces(int tab_size, std::string in) {
std::size_t i = 0;
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
}
return in;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
std::size_t pos = 0;
if (src == dest)
return result;
while ((pos = result.find(src, pos)) != std::string::npos) {
result.replace(pos, src.size(), dest);
pos += dest.length();
}
return result;
}
std::string UTF16ToUTF8(std::u16string_view input) { std::string UTF16ToUTF8(std::u16string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(input.data(), input.data() + input.size()); return convert.to_bytes(input.data(), input.data() + input.size());
+56 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
@@ -16,6 +16,12 @@
namespace Common { namespace Common {
/// Make a string lowercase
[[nodiscard]] std::string ToLower(const std::string_view sv);
/// Make a string uppercase
[[nodiscard]] std::string ToUpper(const std::string_view sv);
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept { [[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
} }
@@ -23,8 +29,37 @@ namespace Common {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
} }
/// Turns " hej " into "hej". Also handles tabs.
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
const std::size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
return {};
}
/// "\"hello\"" is turned to "hello"
/// This one assumes that the string has already been space stripped in both
/// ends, as done by StripSpaces above, for example.
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
return std::string{s.substr(1, s.size() - 2)};
return std::string{s};
}
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
return value ? "True" : "False";
}
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" // "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension);
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
const std::string& dest);
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input); [[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input); [[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
@@ -38,13 +73,30 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length); [[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
/**
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
* `other` for equality.
*/
template <typename InIt>
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
for (; begin != end && *other != '\0'; ++begin, ++other) {
if (*begin != *other) {
return false;
}
}
// Only return true if both strings finished at the same point
return (begin == end) == (*other == '\0');
}
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't /// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
/// NUL-terminated then the string ends at max_len characters. /// NUL-terminated then the string ends at max_len characters.
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len); [[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
std::size_t max_len);
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't /// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to /// null-terminated, then the string ends at the greatest multiple of two less then or equal to
/// max_len_bytes. /// max_len_bytes.
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len); [[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len);
} // namespace Common } // namespace Common
+1
View File
@@ -13,6 +13,7 @@
#include <dynarmic/interface/A64/a64.h> #include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/code_page.h> #include <dynarmic/interface/code_page.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h" #include "dynarmic/interface/A64/config.h"
+45 -23
View File
@@ -110,8 +110,13 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system}, : kernel{system}
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {} , fs_controller{system}
, hid_core{kernel}
, cpu_manager{system}
, reporter{system}
, profile_manager{}
{}
u64 program_id; u64 program_id;
@@ -124,6 +129,12 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
applet_manager.emplace(system);
frontend_applets.emplace(system);
apm_controller.emplace(core_timing);
arp_manager.emplace();
profile_manager.emplace();
// Create a default fs if one doesn't already exist. // Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) { if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -133,7 +144,7 @@ struct System::Impl {
} }
// Create default implementations of applets if one is not provided. // Create default implementations of applets if one is not provided.
frontend_applets.SetDefaultAppletsIfMissing(); frontend_applets->SetDefaultAppletsIfMissing();
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -376,7 +387,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
@@ -428,13 +439,27 @@ struct System::Impl {
core_timing.SyncPause(false); core_timing.SyncPause(false);
Network::CancelPendingSocketOperations(); Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true); kernel.SuspendEmulation(true);
kernel.CloseServices();
kernel.ShutdownCores(); kernel.ShutdownCores();
// Notify services helpers of shutdown
audio_core->Shutdown();
// Wait for threads/services to join
kernel.CloseServices();
// service shutdown
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
frontend_applets.reset();
applet_manager.reset();
apm_controller.reset();
arp_manager.reset();
profile_manager.reset();
fs_controller.Reset(); fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
core_timing.ClearPendingEvents(); core_timing.ClearPendingEvents();
core_timing.Reset();
app_loader.reset(); app_loader.reset();
audio_core.reset(); audio_core.reset();
gpu_core.reset(); gpu_core.reset();
@@ -451,9 +476,6 @@ struct System::Impl {
room_member->SendGameInfo(game_info); room_member->SendGameInfo(game_info);
} }
// Reset all glue registrations
arp_manager.ResetAll();
LOG_DEBUG(Core, "Shutdown OK"); LOG_DEBUG(Core, "Shutdown OK");
} }
@@ -482,13 +504,13 @@ struct System::Impl {
CpuManager cpu_manager; CpuManager cpu_manager;
Reporter reporter; Reporter reporter;
/// Applets /// Applets
Service::AM::AppletManager applet_manager; std::optional<Service::AM::AppletManager> applet_manager;
Service::AM::Frontend::FrontendAppletHolder frontend_applets; std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets;
/// APM (Performance) services /// APM (Performance) services
Service::APM::Controller apm_controller{core_timing}; std::optional<Service::APM::Controller> apm_controller;
/// Service State /// Service State
Service::Glue::ARPManager arp_manager; std::optional<Service::Glue::ARPManager> arp_manager;
Service::Account::ProfileManager profile_manager; std::optional<Service::Account::ProfileManager> profile_manager;
/// Network instance /// Network instance
Network::NetworkInstance network_instance; Network::NetworkInstance network_instance;
Core::SpeedLimiter speed_limiter; Core::SpeedLimiter speed_limiter;
@@ -815,19 +837,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
} }
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets.SetFrontendAppletSet(std::move(set)); impl->frontend_applets->SetFrontendAppletSet(std::move(set));
} }
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return impl->frontend_applets; return *impl->frontend_applets;
} }
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return impl->frontend_applets; return *impl->frontend_applets;
} }
Service::AM::AppletManager& System::GetAppletManager() { Service::AM::AppletManager& System::GetAppletManager() {
return impl->applet_manager; return *impl->applet_manager;
} }
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -868,27 +890,27 @@ const Reporter& System::GetReporter() const {
} }
Service::Glue::ARPManager& System::GetARPManager() { Service::Glue::ARPManager& System::GetARPManager() {
return impl->arp_manager; return *impl->arp_manager;
} }
const Service::Glue::ARPManager& System::GetARPManager() const { const Service::Glue::ARPManager& System::GetARPManager() const {
return impl->arp_manager; return *impl->arp_manager;
} }
Service::APM::Controller& System::GetAPMController() { Service::APM::Controller& System::GetAPMController() {
return impl->apm_controller; return *impl->apm_controller;
} }
const Service::APM::Controller& System::GetAPMController() const { const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller; return *impl->apm_controller;
} }
Service::Account::ProfileManager& System::GetProfileManager() { Service::Account::ProfileManager& System::GetProfileManager() {
return impl->profile_manager; return *impl->profile_manager;
} }
const Service::Account::ProfileManager& System::GetProfileManager() const { const Service::Account::ProfileManager& System::GetProfileManager() const {
return impl->profile_manager; return *impl->profile_manager;
} }
void System::SetExitLocked(bool locked) { void System::SetExitLocked(bool locked) {
+2 -2
View File
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
void CoreTiming::Reset() { void CoreTiming::Reset() {
paused = true; paused = true;
pause_event.Set();
event.Set();
if (timer_thread.joinable()) { if (timer_thread.joinable()) {
timer_thread.request_stop(); timer_thread.request_stop();
pause_event.Set();
event.Set();
timer_thread.join(); timer_thread.join();
} }
has_started = false; has_started = false;
+19 -12
View File
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
UNREACHABLE(); UNREACHABLE();
} }
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { void CpuManager::RunThread(std::stop_token token, std::size_t core) {
/// Initialization /// Initialization
system.RegisterCoreThread(core); system.RegisterCoreThread(core);
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"}; std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
@@ -178,19 +178,26 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
auto& data = core_data[core]; auto& data = core_data[core];
data.host_context = Common::Fiber::ThreadToFiber(); data.host_context = Common::Fiber::ThreadToFiber();
// Cleanup
SCOPE_EXIT {
data.host_context->Exit();
};
// Running // Running
gpu_barrier->arrive_and_wait(); if (!gpu_barrier->Sync(token)) {
if (!stop_token.stop_requested()) { return;
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
} }
data.host_context->Exit();
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
} }
} // namespace Core } // namespace Core
+2 -3
View File
@@ -8,7 +8,6 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <barrier>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <thread> #include <thread>
@@ -53,7 +52,7 @@ public:
} }
void OnGpuReady() { void OnGpuReady() {
gpu_barrier->arrive_and_wait(); gpu_barrier->Sync();
} }
void Initialize(); void Initialize();
@@ -96,7 +95,7 @@ private:
static constexpr std::size_t max_cycle_runs = 5; static constexpr std::size_t max_cycle_runs = 5;
std::optional<std::barrier<>> gpu_barrier{}; std::optional<Common::Barrier> gpu_barrier{};
struct CoreData { struct CoreData {
std::shared_ptr<Common::Fiber> host_context; std::shared_ptr<Common::Fiber> host_context;
std::jthread host_thread; std::jthread host_thread;
+1 -2
View File
@@ -14,7 +14,6 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/evp.h> #include <openssl/evp.h>
#include "common/fs/file.h" #include "common/fs/file.h"
@@ -623,7 +622,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
Key128 key = Common::HexStringToArray<16>(out[1]); Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
} else { } else {
boost::algorithm::to_lower(out[0]); out[0] = Common::ToLower(out[0]);
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) { if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
const auto& index = iter128->second; const auto& index = iter128->second;
const Key128 key = Common::HexStringToArray<16>(out[1]); const Key128 key = Common::HexStringToArray<16>(out[1]);
+5 -3
View File
@@ -6,7 +6,6 @@
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/hex_util.h" #include "common/hex_util.h"
@@ -42,13 +41,16 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32; const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) { static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
const auto upper = boost::algorithm::to_upper_copy(name); const std::string& name) {
const auto upper = Common::ToUpper(name);
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) { for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
if (dir->GetFile(fname) != nullptr) { if (dir->GetFile(fname) != nullptr) {
return dir->GetFile(fname); return dir->GetFile(fname);
} }
} }
return nullptr; return nullptr;
} }
+3 -2
View File
@@ -8,7 +8,6 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/hex_util.h" #include "common/hex_util.h"
@@ -74,10 +73,12 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
#else #else
const auto subdirs = dir->GetSubdirectories(); const auto subdirs = dir->GetSubdirectories();
for (const auto& subdir : subdirs) { for (const auto& subdir : subdirs) {
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) { std::string dir_name = Common::ToLower(subdir->GetName());
if (dir_name == name) {
return subdir; return subdir;
} }
} }
return nullptr; return nullptr;
#endif #endif
} }
+4 -4
View File
@@ -8,7 +8,6 @@
#include <limits> #include <limits>
#include <random> #include <random>
#include <regex> #include <regex>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/evp.h> #include <openssl/evp.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
@@ -1415,10 +1414,11 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
continue; continue;
} }
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1)); const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
if (ext == "nsp") {
if (extension == "nsp") {
ProcessNSP(file); ProcessNSP(file);
} else if (ext == "xci") { } else if (extension == "xci") {
ProcessXCI(file); ProcessXCI(file);
} }
} }
+2 -3
View File
@@ -9,7 +9,6 @@
#include <regex> #include <regex>
#include <string> #include <string>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/evp.h> #include <openssl/evp.h>
@@ -64,8 +63,8 @@ NAX::NAX(VirtualFile file_)
return; return;
} }
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]}); const std::string two_dir = Common::ToUpper(std::string{match[1]});
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]}); const std::string nca_id = Common::ToLower(std::string{match[2]});
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id)); status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
} }
+2 -11
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,14 +19,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
// Virtual to Physical core map. // Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3,
}; };
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>(); static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
@@ -72,11 +72,9 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept { void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked()); ASSERT(this->IsLocked());
if (m_woken_dummy_threads.size() > 0) { for (auto* thread : m_woken_dummy_threads)
for (auto* thread : m_woken_dummy_threads) thread->DummyThreadEndWait(kernel);
thread->DummyThreadEndWait(kernel); m_woken_dummy_threads.clear();
m_woken_dummy_threads.clear();
}
} }
} // namespace Kernel } // namespace Kernel
-1
View File
@@ -59,7 +59,6 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
KThread* cur_child = nullptr; KThread* cur_child = nullptr;
{ {
KScopedLightLock proc_lk(process->GetListLock()); KScopedLightLock proc_lk(process->GetListLock());
auto& thread_list = process->GetThreadList(); auto& thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
+4 -10
View File
@@ -1236,18 +1236,13 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request. // Determine if this is the first termination request.
const bool first_request = [&]() -> bool { // Perform an atomic compare-and-swap from false to true.
// Perform an atomic compare-and-swap from false to true. bool expected = false;
bool expected = false;
return m_termination_requested.compare_exchange_strong(expected, true);
}();
// If this is the first request, start termination procedure. // If this is the first request, start termination procedure.
if (first_request) { if (m_termination_requested.compare_exchange_strong(expected, true)) {
// If the thread is in initialized state, just change state to terminated. // If the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) { if (this->GetState() == ThreadState::Initialized) {
m_thread_state = ThreadState::Terminated; return m_thread_state = ThreadState::Terminated;
return ThreadState::Terminated;
} }
// Register the terminating dpc. // Register the terminating dpc.
@@ -1281,7 +1276,6 @@ namespace Kernel {
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true); m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
} }
} }
return this->GetState(); return this->GetState();
} }
+34 -5
View File
@@ -4,7 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "common/thread.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task.h" #include "core/hle/kernel/k_worker_task.h"
@@ -26,7 +28,33 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
} }
} }
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} KWorkerTaskManager::KWorkerTaskManager(KernelCore& kernel) {
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
Common::SetCurrentThreadName("KWorkerManager");
while (!stop_token.stop_requested()) {
KWorkerTask* t;
{
std::unique_lock lk{m_task_mutex};
m_task_cv.wait(lk, m_waiting_thread.get_stop_token(), [&]() {
return !m_task_queue.empty();
});
if (stop_token.stop_requested())
break;
t = m_task_queue.back();
m_task_queue.pop_back();
}
t->DoWorkerTask(kernel);
}
});
}
KWorkerTaskManager::~KWorkerTaskManager() {
if (m_waiting_thread.joinable()) {
m_waiting_thread.request_stop();
m_task_cv.notify_one();
m_waiting_thread.join();
}
}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count); ASSERT(type <= WorkerType::Count);
@@ -35,10 +63,11 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
m_waiting_thread.QueueWork([&kernel, task]() { {
// Do the task. std::scoped_lock lk{m_task_mutex};
task->DoWorkerTask(kernel); m_task_queue.emplace_back(task);
}); }
m_task_cv.notify_one();
} }
} // namespace Kernel } // namespace Kernel
+11 -4
View File
@@ -1,8 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include <thread>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
@@ -18,15 +23,17 @@ public:
Count, Count,
}; };
KWorkerTaskManager(); KWorkerTaskManager(KernelCore& kernel);
~KWorkerTaskManager();
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task); static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
private: private:
void AddTask(KernelCore& kernel, KWorkerTask* task); void AddTask(KernelCore& kernel, KWorkerTask* task);
std::jthread m_waiting_thread;
private: std::mutex m_task_mutex;
Common::ThreadWorker m_waiting_thread; std::condition_variable_any m_task_cv;
std::vector<KWorkerTask*> m_task_queue;
}; };
} // namespace Kernel } // namespace Kernel
+17 -12
View File
@@ -80,7 +80,10 @@ struct KernelCore::Impl {
// so it will be statically given a TLS slot anyways. // so it will be statically given a TLS slot anyways.
static inline thread_local ThreadLocalData tls_data = {}; static inline thread_local ThreadLocalData tls_data = {};
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} { explicit Impl(Core::System& system_, KernelCore& kernel_)
: worker_task_manager{kernel_}
, system{system_}
{
tls_data.lock = true; tls_data.lock = true;
} }
@@ -967,12 +970,16 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer; return *impl->hardware_timer;
} }
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
return *impl->global_object_list_container; if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container; if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
void KernelCore::PrepareReschedule(std::size_t id) { void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1001,16 +1008,10 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get(); auto* manager = server_manager.get();
if (!impl->is_shutting_down) {
{
std::scoped_lock lk{impl->server_lock}; std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager)); impl->server_managers.emplace_back(std::move(server_manager));
} }
manager->LoopProcess(); manager->LoopProcess();
} }
@@ -1255,6 +1256,10 @@ void KernelCore::SuspendEmulation(bool suspended) {
} }
void KernelCore::ShutdownCores() { void KernelCore::ShutdownCores() {
// Notify shutdown (pre-emptively)
for (auto& sm : impl->server_managers) {
sm->NotifyShutdown();
}
impl->TerminateAllProcesses(); impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this}; KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) for (auto* thread : impl->shutdown_threads)
+2 -3
View File
@@ -175,9 +175,8 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread. /// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id); void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer& ObjectListContainer(); KAutoObjectWithListContainer* ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
const KAutoObjectWithListContainer& ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking /// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown. /// leaks after emulation has been shutdown.
+4 -3
View File
@@ -151,7 +151,8 @@ public:
const bool is_initialized = this->IsInitialized(); const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0; uintptr_t arg = 0;
if (is_initialized) { if (is_initialized) {
kernel.ObjectListContainer().Unregister(this); if (auto const olc = kernel.ObjectListContainer(); olc)
olc->Unregister(this);
arg = this->GetPostDestroyArgument(); arg = this->GetPostDestroyArgument();
this->Finalize(kernel); this->Finalize(kernel);
} }
@@ -175,7 +176,7 @@ public:
public: public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) { static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size); kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer().Initialize(); kernel.ObjectListContainer()->Initialize();
} }
static Derived* Create(KernelCore& kernel) { static Derived* Create(KernelCore& kernel) {
@@ -186,7 +187,7 @@ public:
} }
static void Register(KernelCore& kernel, Derived* obj) { static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer().Register(obj); return kernel.ObjectListContainer()->Register(obj);
} }
static size_t GetObjectSize(KernelCore& kernel) { static size_t GetObjectSize(KernelCore& kernel) {
+5 -6
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,13 +18,9 @@ void LoopProcess(Core::System& system) {
WindowSystem window_system(system); WindowSystem window_system(system);
ButtonPoller button_poller(system, window_system); ButtonPoller button_poller(system, window_system);
EventObserver event_observer(system, window_system); EventObserver event_observer(system, window_system);
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService( server_manager->RegisterNamedService("appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService(
"appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+9 -8
View File
@@ -26,24 +26,25 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
m_window_system.SetEventObserver(this); m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent)); m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
m_thread = std::jthread([this](std::stop_token stop_token) { system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() {
Common::SetCurrentThreadName("am:EventObserver"); auto const stop_token = m_stop_source.get_token();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token); auto* signaled_holder = this->WaitSignaled(stop_token);
if (!signaled_holder) if (stop_token.stop_requested() || !signaled_holder)
break; break;
this->Process(signaled_holder); this->Process(signaled_holder);
} }
m_process_cv.notify_one();
}); });
} }
EventObserver::~EventObserver() { EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish. // Signal thread and wait for processing to finish.
if (m_thread.joinable()) { m_stop_source.request_stop();
// Signal thread and wait for processing to finish. m_wakeup_event.Signal(m_system.Kernel());
m_thread.request_stop(); {
m_wakeup_event.Signal(m_system.Kernel()); std::unique_lock lk{m_process_mutex};
m_thread.join(); m_process_cv.wait(lk);
} }
// Free remaining owned sessions. // Free remaining owned sessions.
+5 -1
View File
@@ -6,6 +6,8 @@
#pragma once #pragma once
#include <condition_variable>
#include <stop_token>
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
@@ -62,7 +64,9 @@ private:
MultiWait m_deferred_wait_list; MultiWait m_deferred_wait_list;
// Processing thread. // Processing thread.
std::jthread m_thread{}; std::stop_source m_stop_source{};
std::mutex m_process_mutex;
std::condition_variable m_process_cv{};
}; };
} // namespace Service::AM } // namespace Service::AM
+4 -5
View File
@@ -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
@@ -90,10 +90,9 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
} }
TimeManager::TimeManager(Core::System& system) TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{ : m_steady_clock_resource{system}, m_time_zone_binary{system}
system, , m_worker{system, m_steady_clock_resource, m_file_timestamp_worker}
m_steady_clock_resource, {
m_file_timestamp_worker} {
m_time_m = m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
+151 -151
View File
@@ -20,15 +20,17 @@
namespace Service::Glue::Time { namespace Service::Glue::Time {
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker)
FileTimestampWorker& file_timestamp_worker) : m_system{system}
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent( , m_ctx{m_system, "Glue:TimeWorker"}
"Glue:TimeWorker:Event")}, , m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")}
m_steady_clock_resource{steady_clock_resource}, , m_steady_clock_resource{steady_clock_resource}
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( , m_file_timestamp_worker{file_timestamp_worker}
"Glue:TimeWorker:SteadyClockTimerEvent")}, , m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")}
m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}, , m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} { , m_alarm_worker{m_system, m_steady_clock_resource}
, m_pm_state_change_handler{m_alarm_worker}
{
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
"Time::SteadyClockEvent", "Time::SteadyClockEvent",
[this](s64 time, [this](s64 time,
@@ -47,14 +49,16 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
} }
TimeWorker::~TimeWorker() { TimeWorker::~TimeWorker() {
// Wait for processing to stop
m_stop_source.request_stop();
m_local_clock_event->Signal(m_system.Kernel()); m_local_clock_event->Signal(m_system.Kernel());
m_network_clock_event->Signal(m_system.Kernel()); m_network_clock_event->Signal(m_system.Kernel());
m_ephemeral_clock_event->Signal(m_system.Kernel()); m_ephemeral_clock_event->Signal(m_system.Kernel());
std::this_thread::sleep_for(std::chrono::milliseconds(16));
m_thread.request_stop();
m_event->Signal(m_system.Kernel()); m_event->Signal(m_system.Kernel());
m_thread.join(); {
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
}
m_ctx.CloseEvent(m_event); m_ctx.CloseEvent(m_event);
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
@@ -122,166 +126,162 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
} }
void TimeWorker::StartThread() { void TimeWorker::StartThread() {
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this)); m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() {
} auto const stop_token = m_stop_source.get_token();
while (!stop_token.stop_requested()) {
void TimeWorker::ThreadFunc(std::stop_token stop_token) { enum class EventType : s32 {
Common::SetCurrentThreadName("TimeWorker"); Exit = 0,
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); PowerStateChange = 1,
SignalAlarms = 2,
while (!stop_token.stop_requested()) { UpdateLocalSystemClock = 3,
enum class EventType : s32 { UpdateNetworkSystemClock = 4,
Exit = 0, UpdateEphemeralSystemClock = 5,
PowerStateChange = 1, UpdateSteadyClock = 6,
SignalAlarms = 2, UpdateFileTimestamp = 7,
UpdateLocalSystemClock = 3, AutoCorrect = 8,
UpdateNetworkSystemClock = 4, };
UpdateEphemeralSystemClock = 5, s32 index{};
UpdateSteadyClock = 6, if (m_pm_state_change_handler.m_priority != 0) {
UpdateFileTimestamp = 7, // TODO: gIPmModuleService::GetEvent() 1
AutoCorrect = 8, index = WaitAny(
}; m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
s32 index{}; &m_alarm_worker.GetEvent() // 1
);
if (m_pm_state_change_handler.m_priority != 0) { } else {
// TODO: gIPmModuleService::GetEvent() 1 // TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(), index = WaitAny(
&m_event->GetReadableEvent(), // 0 m_system.Kernel(),
&m_alarm_worker.GetEvent() // 1 &m_event->GetReadableEvent(), // 0
); &m_alarm_worker.GetEvent(), // 1
} else { &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
// TODO: gIPmModuleService::GetEvent() 1 m_local_clock_event, // 3
index = WaitAny(m_system.Kernel(), m_network_clock_event, // 4
&m_event->GetReadableEvent(), // 0 m_ephemeral_clock_event, // 5
&m_alarm_worker.GetEvent(), // 1 &m_timer_steady_clock->GetReadableEvent(), // 6
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 &m_timer_file_system->GetReadableEvent(), // 7
m_local_clock_event, // 3 m_standard_user_auto_correct_clock_event // 8
m_network_clock_event, // 4 );
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
} }
break;
case EventType::SignalAlarms: if (stop_token.stop_requested())
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); break;
m_time_m->CheckAndSignalAlarms();
break;
case EventType::UpdateLocalSystemClock: { switch (EventType(index)) {
m_local_clock_event->Clear(m_system.Kernel()); case EventType::Exit:
return;
Service::PSC::Time::SystemClockContext context{}; case EventType::PowerStateChange:
R_ASSERT(m_local_clock->GetSystemClockContext(&context)); m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
}
break;
m_set_sys->SetUserSystemClockContext(context); case EventType::SignalAlarms:
m_file_timestamp_worker.SetFilesystemPosixTime(); m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
break; m_time_m->CheckAndSignalAlarms();
} break;
case EventType::UpdateNetworkSystemClock: { case EventType::UpdateLocalSystemClock: {
m_network_clock_event->Clear(m_system.Kernel()); m_local_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{}; Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_network_clock->GetSystemClockContext(&context)); R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context); m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
s64 time{};
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
break; break;
} }
[[maybe_unused]] auto offset_before{ case EventType::UpdateNetworkSystemClock: {
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0}; m_network_clock_event->Clear(m_system.Kernel());
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime(); Service::PSC::Time::SystemClockContext context{};
break; R_ASSERT(m_network_clock->GetSystemClockContext(&context));
}
case EventType::UpdateEphemeralSystemClock: { m_set_sys->SetNetworkSystemClockContext(context);
m_ephemeral_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{}; s64 time{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context); if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
if (res != ResultSuccess) { break;
}
[[maybe_unused]] auto offset_before{
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime();
break; break;
} }
s64 time{}; case EventType::UpdateEphemeralSystemClock: {
res = m_ephemeral_clock->GetCurrentTime(&time); m_ephemeral_clock_event->Clear(m_system.Kernel());
if (res != ResultSuccess) {
Service::PSC::Time::SystemClockContext context{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
if (res != ResultSuccess) {
break;
}
s64 time{};
res = m_ephemeral_clock->GetCurrentTime(&time);
if (res != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
? m_report_ephemeral_clock_context.offset
: 0};
// TODO system report "ephemeral_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context;
if (!m_ig_report_ephemeral_clock_context_set) {
m_ig_report_ephemeral_clock_context_set = true;
}
break; break;
} }
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set case EventType::UpdateSteadyClock:
? m_report_ephemeral_clock_context.offset m_timer_steady_clock->Clear(m_system.Kernel());
: 0};
// TODO system report "ephemeral_netclock_operation" m_steady_clock_resource.UpdateTime();
// "clock_time" = time m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
// "context_offset_before" = offset_before break;
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context; case EventType::UpdateFileTimestamp:
if (!m_ig_report_ephemeral_clock_context_set) { m_timer_file_system->Clear(m_system.Kernel());
m_ig_report_ephemeral_clock_context_set = true; m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
} }
break;
} }
m_process_cv.notify_one();
case EventType::UpdateSteadyClock: });
m_timer_steady_clock->Clear(m_system.Kernel());
m_steady_clock_resource.UpdateTime();
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
break;
case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
}
}
} }
} // namespace Service::Glue::Time } // namespace Service::Glue::Time
+8 -4
View File
@@ -1,8 +1,12 @@
// 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
#pragma once #pragma once
#include <condition_variable>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h" #include "core/hle/service/glue/time/alarm_worker.h"
@@ -33,17 +37,17 @@ public:
void StartThread(); void StartThread();
private:
template <typename T> template <typename T>
T GetSettingsItemValue(const std::string& category, const std::string& name); T GetSettingsItemValue(const std::string& category, const std::string& name);
void ThreadFunc(std::stop_token stop_token);
Core::System& m_system; Core::System& m_system;
KernelHelpers::ServiceContext m_ctx; KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::jthread m_thread; std::mutex m_process_mutex;
std::condition_variable m_process_cv;
std::stop_source m_stop_source;
Kernel::KEvent* m_event{}; Kernel::KEvent* m_event{};
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
+12 -3
View File
@@ -98,8 +98,10 @@ ServerManager::~ServerManager() {
// Signal stop. // Signal stop.
m_stop_source.request_stop(); m_stop_source.request_stop();
m_wakeup_event->Signal(m_system.Kernel()); m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
// Wait for processing to stop.
// Wait for processing to stop. Schedule
m_stopped.Wait(); m_stopped.Wait();
m_threads.clear(); m_threads.clear();
@@ -253,6 +255,14 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
} }
} }
/// @brief Notifies that the system is shutting down (pre-emptively terminate threads)
void ServerManager::NotifyShutdown() {
m_stop_source.request_stop();
// Wake them up regardless
m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
}
Result ServerManager::LoopProcess() { Result ServerManager::LoopProcess() {
SCOPE_EXIT { SCOPE_EXIT {
m_stopped.Set(); m_stopped.Set();
@@ -285,9 +295,8 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
this->LinkDeferred(); this->LinkDeferred();
// If we're done, return before we start waiting. // If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) { if (m_stop_source.stop_requested())
return nullptr; return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) { if (selected == std::addressof(*m_wakeup_holder)) {
+4
View File
@@ -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
@@ -49,6 +52,7 @@ public:
Result LoopProcess(); Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads); void StartAdditionalHostThreads(const char* name, size_t num_threads);
void NotifyShutdown();
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
+1 -1
View File
@@ -117,7 +117,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"friends", &Friend::LoopProcess}, {"friends", &Friend::LoopProcess},
{"settings", &Set::LoopProcess}, {"settings", &Set::LoopProcess},
{"psc", &PSC::LoopProcess}, {"psc", &PSC::LoopProcess},
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess}, {"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess}, {"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess}, {"jit", &JIT::LoopProcess},
@@ -153,6 +152,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"usb", &USB::LoopProcess}, {"usb", &USB::LoopProcess},
{"i2c", &I2C::LoopProcess}, {"i2c", &I2C::LoopProcess},
{"gpio", &GPIO::LoopProcess}, {"gpio", &GPIO::LoopProcess},
{"glue", &Glue::LoopProcess},
}) })
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
} }
+4 -6
View File
@@ -305,16 +305,14 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
SM::~SM() = default; SM::~SM() = default;
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
Kernel::KEvent* deferral_event{}; Kernel::KEvent* deferral_event{};
server_manager->ManageDeferral(&deferral_event); server_manager->ManageDeferral(&deferral_event);
service_manager.SetDeferralEvent(deferral_event); system.ServiceManager().SetDeferralEvent(deferral_event);
auto sm_service = std::make_shared<SM>(system.ServiceManager(), system); auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
server_manager->ManageNamedPort("sm:", [sm_service] { return sm_service; }); server_manager->ManageNamedPort("sm:", [sm_service] {
return sm_service;
});
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+23 -33
View File
@@ -11,6 +11,7 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/vi/conductor.h" #include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h" #include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h" #include "core/hle/service/vi/display_list.h"
@@ -29,24 +30,29 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
}); });
if (system.IsMulticore()) { if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent( m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"ScreenComposition", m_signal.Set();
[this](s64 time, return std::chrono::nanoseconds(this->GetNextTicks());
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { });
m_signal.Set();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); }); m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() {
} else { auto const stop_token = m_thread.get_stop_token();
m_event = Core::Timing::CreateEvent( Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
"ScreenComposition", Common::SetCurrentThreadToPerformanceCores();
[this](s64 time, while (!stop_token.stop_requested()) {
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { m_signal.Wait();
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
break;
}
this->ProcessVsync(); this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks()); }
}); });
} else {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
} }
@@ -54,10 +60,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
Conductor::~Conductor() { Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event); m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_thread.joinable()) {
if (m_system.IsMulticore()) {
m_thread.request_stop(); m_thread.request_stop();
m_signal.Set(); m_signal.Set();
m_thread.join();
} }
} }
@@ -83,22 +89,6 @@ void Conductor::ProcessVsync() {
} }
} }
void Conductor::VsyncThread(std::stop_token token) {
Common::SetCurrentThreadName("VSyncThread");
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!token.stop_requested()) {
m_signal.Wait();
if (m_system.IsShuttingDown()) {
return;
}
this->ProcessVsync();
}
}
s64 Conductor::GetNextTicks() const { s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values; const auto& settings = Settings::values;
auto speed_scale = 1.f; auto speed_scale = 1.f;
+2 -2
View File
@@ -10,7 +10,6 @@
#include <string> #include <string>
#include <concepts> #include <concepts>
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/concepts.h" #include "common/concepts.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging.h" #include "common/logging.h"
@@ -171,7 +170,8 @@ FileType GuessFromFilename(const std::string& name) {
else if (name == "00") else if (name == "00")
return FileType::NCA; return FileType::NCA;
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name))); auto const extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
if (extension == "nro") if (extension == "nro")
return FileType::NRO; return FileType::NRO;
else if (extension == "nso") else if (extension == "nso")
+14
View File
@@ -1,6 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// gcc14 bug
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h" #include "core/memory/dmnt_cheat_types.h"
@@ -1266,3 +1275,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
} // namespace Core::Memory } // namespace Core::Memory
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
+11 -9
View File
@@ -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 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -254,14 +254,16 @@ struct UnrecognizedInstruction {
struct CheatVmOpcode { struct CheatVmOpcode {
bool begin_conditional_block{}; bool begin_conditional_block{};
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, std::variant<
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, std::monostate,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction> BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
opcode{}; SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
> opcode{};
}; };
class DmntCheatVm { class DmntCheatVm {
+1 -2
View File
@@ -14,7 +14,6 @@
#include <regex> #include <regex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <boost/algorithm/string/trim.hpp>
#ifdef _WIN32 #ifdef _WIN32
// windows.h needs to be included before shellapi.h // windows.h needs to be included before shellapi.h
@@ -133,7 +132,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
std::string line; std::string line;
std::getline(file, line); std::getline(file, line);
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end()); line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
boost::trim(line); line = Common::StripSpaces(line);
if (line.empty()) { if (line.empty()) {
// An empty line marks start of the IP ban list // An empty line marks start of the IP ban list
ban_list_type = true; ban_list_type = true;
@@ -170,13 +170,19 @@ template<>
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
code.bt(page, *ctx.conf.page_table_marked_bit); auto const marked_bit = *ctx.conf.page_table_marked_bit;
code.jc(abort, code.T_NEAR); if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) {
code.test(page, s32(1 << marked_bit));
code.jnz(abort, code.T_NEAR);
} else {
code.bt(page, marked_bit);
code.jc(abort, code.T_NEAR);
}
} }
// mask away attributes // mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) { if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) { } else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask); code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.mov(tmp, ctx.conf.page_table_pointer_mask);
@@ -79,8 +79,10 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; if (auto const shfmt = data->shared_memory_format; shfmt) {
UpdateButtonLifo(npad_entry, data->aruid); auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
@@ -88,19 +90,19 @@ void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; if (auto const shfmt = data->shared_memory_format; shfmt) {
UpdateButtonLifo(npad_entry, data->aruid); auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) {
if (data == nullptr) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
return; UpdateButtonLifo(npad_entry, aruid);
} }
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid);
} }
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
+1 -2
View File
@@ -8,7 +8,6 @@
#include <string> #include <string>
#include <QEventLoop> #include <QEventLoop>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include "common/httplib.h" #include "common/httplib.h"
@@ -46,7 +45,7 @@ void DiscordImpl::Pause() {
std::string DiscordImpl::GetGameString(const std::string& title) { std::string DiscordImpl::GetGameString(const std::string& title) {
// Convert to lowercase // Convert to lowercase
std::string icon_name = boost::algorithm::to_lower_copy(title); std::string icon_name = Common::ToLower(title);
// Replace spaces with dashes // Replace spaces with dashes
std::replace(icon_name.begin(), icon_name.end(), ' ', '-'); std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
+5 -2
View File
@@ -66,14 +66,17 @@ void EmuThread::run() {
} else { } else {
QtCommon::system->Pause(); QtCommon::system->Pause();
m_stopped.Set(); m_stopped.Set();
EmulationPaused(lk); EmulationPaused(lk);
m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; }); m_should_run_cv.wait(lk, stop_token, [&] {
return m_should_run;
});
EmulationResumed(lk); EmulationResumed(lk);
} }
} }
// Shutdown the main emulated process // Shutdown the main emulated process
// Needs to pause first, we may not have paused before shutting down.
QtCommon::system->Pause();
QtCommon::system->DetachDebugger(); QtCommon::system->DetachDebugger();
QtCommon::system->ShutdownMainProcess(); QtCommon::system->ShutdownMainProcess();
} }
@@ -496,31 +496,30 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
// Flatten all leading PHIs from each block into a vector // Flatten all leading PHIs from each block into a vector
std::vector<IR::Inst*> phi_instructions; std::vector<IR::Inst*> phi_instructions;
for (IR::Block* block : program.blocks) { for (IR::Block* block : program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) { for (auto it = block->begin(); it != block->end(); ++it) {
if (it->GetOpcode() != IR::Opcode::Phi) if (it->GetOpcode() != IR::Opcode::Phi)
break; break;
phi_instructions.push_back(&*it); phi_instructions.push_back(&*it);
}
}
if (phi_instructions.empty()) {
return; // nothing to patch
}
// Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = static_cast<size_t>(-1);
ctx.PatchDeferredPhi([&](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_instructions[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
} }
}
if (phi_instructions.empty()) {
return; // nothing to patch
}
// Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = size_t(-1);
ctx.PatchDeferredPhi([&ctx, &phi_index, phi_insts = std::move(phi_instructions)](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_insts[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
}
} // Anonymous namespace } // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) { std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -55,7 +55,7 @@ size_t StagingBuffers::RequestBuffer(size_t requested_size) {
} }
StagingBufferAlloc alloc; StagingBufferAlloc alloc;
alloc.buffer.Create(); alloc.buffer.Create();
const auto next_pow2_size = std::bit_ceil(requested_size); const auto next_pow2_size = Common::NextPow2(requested_size);
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr, glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
storage_flags | GL_MAP_PERSISTENT_BIT); storage_flags | GL_MAP_PERSISTENT_BIT);
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size, alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
@@ -1404,7 +1404,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
const u32 copy_size = region.width * region.height * region.depth * img_bpp; const u32 copy_size = region.width * region.height * region.depth * img_bpp;
if (pbo_size < copy_size) { if (pbo_size < copy_size) {
intermediate_pbo.Create(); intermediate_pbo.Create();
pbo_size = std::bit_ceil(copy_size); pbo_size = Common::NextPow2(copy_size);
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY); glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
} }
// Copy from source to PBO // Copy from source to PBO
@@ -1011,7 +1011,7 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
if (buffers[level]) { if (buffers[level]) {
return *buffers[level]; return *buffers[level];
} }
const auto new_size = std::bit_ceil(needed_size); const auto new_size = Common::NextPow2(needed_size);
static constexpr VkBufferUsageFlags flags = static constexpr VkBufferUsageFlags flags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+1 -1
View File
@@ -2190,7 +2190,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
image.flags &= ~ImageFlagBits::BadOverlap; image.flags &= ~ImageFlagBits::BadOverlap;
lru_cache.Free(image.lru_index); lru_cache.Free(image.lru_index);
const auto& clear_page_table = const auto& clear_page_table =
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>>& selected_page_table) { [image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& selected_page_table) {
const auto page_it = selected_page_table.find(page); const auto page_it = selected_page_table.find(page);
if (page_it == selected_page_table.end()) { if (page_it == selected_page_table.end()) {
ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS); ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS);
@@ -22,6 +22,7 @@
#include <queue> #include <queue>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/hash.h"
#include "common/literals.h" #include "common/literals.h"
#include "common/lru_cache.h" #include "common/lru_cache.h"
#include <ranges> #include <ranges>
@@ -68,7 +69,7 @@ struct AsyncDecodeContext {
std::atomic_bool complete; std::atomic_bool complete;
}; };
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>>; using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
class TextureCacheChannelInfo : public ChannelInfo { class TextureCacheChannelInfo : public ChannelInfo {
public: public:
@@ -442,7 +443,7 @@ private:
u64 last_framebuffer_serial = 0; u64 last_framebuffer_serial = 0;
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers; ::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
::Common::unordered_map<u64, std::vector<ImageMapId>> page_table; ::Common::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views; ::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
DAddr virtual_invalid_space{}; DAddr virtual_invalid_space{};
+122
View File
@@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <regex>
#include <string>
#include "common/logging.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs/vfs_real.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h"
class EmuWindow_Headless : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_Headless() = default;
~EmuWindow_Headless() = default;
bool IsShown() const override {
return true;
}
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override {
}
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return std::make_unique<DummyContext>();
}
};
int main(int argc, char *argv[]) {
struct {
Core::System system{};
EmuWindow_Headless emu_window{};
} state = {};
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
if (argc < 2) {
LOG_CRITICAL(Frontend, "Usage: {} [ms-to-run] [file]", argv[0]);
return EXIT_FAILURE;
}
std::chrono::milliseconds time_quanta = std::chrono::milliseconds{atoi(argv[1])};
auto const time_end = std::chrono::steady_clock::now() + time_quanta;
std::string filepath = argv[2];
// apply the log_filter setting
// the logger was initialized before and doesn't pick up the filter on its own
Common::Log::Filter filter;
filter.ParseFilterString("*:Info");
Common::Log::SetGlobalFilter(filter);
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return EXIT_FAILURE;
}
state.system.Initialize();
InputCommon::InputSubsystem input_subsystem{};
// Apply the command line arguments
state.system.ApplySettings();
Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Null);
state.system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
state.system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
state.system.GetFileSystemController().CreateFactories(*state.system.GetFilesystem());
state.system.GetUserChannel().clear();
Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application,
};
if (auto const load_result = state.system.Load(state.emu_window, filepath, load_parameters); load_result != Core::SystemResultStatus::Success) {
LOG_CRITICAL(Frontend, "load result = {}", u32(load_result));
// shutdown
void(state.system.Pause());
state.system.DetachDebugger();
state.system.ShutdownMainProcess();
return EXIT_FAILURE;
}
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
state.system.GPU().Start();
state.system.GetCpuManager().OnGpuReady();
// don't do anything, SDL3 already exists for us :D
state.system.RegisterExitCallback([] {});
void(state.system.Run());
if (state.system.DebuggerEnabled())
state.system.InitializeDebugger();
while (state.system.IsPoweredOn() && !state.system.GetExitRequested()) {
auto const time_now = std::chrono::steady_clock::now();
if (time_now > time_end)
break;
}
// shutdown
void(state.system.Pause());
state.system.DetachDebugger();
state.system.ShutdownMainProcess();
return EXIT_SUCCESS;
}
#define VMA_IMPLEMENTATION
#include "video_core/vulkan_common/vma.h"