mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-30 03:10:19 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b03ec072f |
Vendored
+579
-624
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+503
-548
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+508
-550
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+527
-573
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId,
|
||||
jstring jupdatePath) {
|
||||
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
||||
for (const auto& item : nsp->GetNCAs()) {
|
||||
for (const auto& nca_details : item.second) {
|
||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
||||
if (update_id == program_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,6 @@
|
||||
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
|
||||
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
|
||||
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
|
||||
<string name="post_processing_add">Añadir efecto</string>
|
||||
<string name="post_processing_open_list">Abrir lista</string>
|
||||
<string name="post_processing_close_list">Cerrar lista</string>
|
||||
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
||||
|
||||
@@ -318,13 +318,6 @@
|
||||
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
||||
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
||||
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
|
||||
<string name="frame_gen_on">Вкл.</string>
|
||||
<string name="frame_gen_off">Выкл.</string>
|
||||
<string name="frame_gen_fixed">Фикс.</string>
|
||||
<string name="frame_gen_flow_auto">Авто</string>
|
||||
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
|
||||
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
|
||||
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
|
||||
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
|
||||
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
||||
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
|
||||
@@ -335,14 +328,12 @@
|
||||
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
|
||||
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
||||
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
|
||||
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
|
||||
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
||||
<string name="lossless_scaling_install">Установить Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
|
||||
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
||||
<string name="frame_generation_support">Генерация кадров</string>
|
||||
<string name="frame_generation_supported">Поддерживается</string>
|
||||
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
|
||||
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
|
||||
<string name="lossless_scaling_installed">Установлена</string>
|
||||
<string name="lossless_scaling_not_installed">Не установлена</string>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
|
||||
<string name="notice_notification_channel_name">错误与注意事项</string>
|
||||
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
||||
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
||||
@@ -291,25 +290,6 @@
|
||||
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
||||
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
||||
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
|
||||
<string name="post_processing">后处理效果</string>
|
||||
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
|
||||
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
|
||||
<string name="post_processing_add">添加效果</string>
|
||||
<string name="post_processing_remove">移除</string>
|
||||
<string name="post_processing_open_list">打开列表</string>
|
||||
<string name="post_processing_close_list">关闭列表</string>
|
||||
<string name="post_processing_remove_all">移除效果</string>
|
||||
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
|
||||
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
|
||||
<string name="post_processing_presets">预设</string>
|
||||
<string name="post_processing_preset_new">新建预设</string>
|
||||
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
|
||||
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
|
||||
<string name="post_processing_preset_none">无预设</string>
|
||||
<string name="post_processing_preset_delete">删除预设</string>
|
||||
<string name="post_processing_preset_reset">重置预设值</string>
|
||||
<string name="post_processing_reset">重置为默认值</string>
|
||||
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
|
||||
<string name="frame_gen">帧生成</string>
|
||||
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
||||
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
|
||||
@@ -324,13 +304,6 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_on">开</string>
|
||||
<string name="frame_gen_off">关</string>
|
||||
<string name="frame_gen_fixed">固定</string>
|
||||
<string name="frame_gen_flow_auto">自动</string>
|
||||
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
|
||||
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
|
||||
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
|
||||
<string name="frame_gen_queue_target">帧队列目标</string>
|
||||
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
||||
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
|
||||
@@ -341,14 +314,12 @@
|
||||
<string name="frame_gen_flow_scale">运动预估分辨率</string>
|
||||
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
||||
<string name="frame_gen_unsupported">帧生成不可用</string>
|
||||
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
|
||||
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
||||
<string name="lossless_scaling_install">安装 Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
|
||||
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
||||
<string name="frame_generation_support">帧生成</string>
|
||||
<string name="frame_generation_supported">支持</string>
|
||||
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
|
||||
<string name="lossless_scaling">无损缩放</string>
|
||||
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
||||
<string name="lossless_scaling_installed">已安装</string>
|
||||
@@ -640,11 +611,6 @@
|
||||
<string name="log">日志记录</string>
|
||||
<string name="flush_by_line">按行刷新调试日志</string>
|
||||
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
|
||||
<string name="extended_logging">开启扩展日志</string>
|
||||
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
|
||||
<string name="log_filter">日志筛选器</string>
|
||||
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU 日志</string>
|
||||
<string name="gpu_log_level">日志等级</string>
|
||||
@@ -928,8 +894,6 @@
|
||||
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
||||
|
||||
<string name="loader_requires_firmware">游戏需要固件</string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
|
||||
|
||||
<!-- Intent Launch strings -->
|
||||
<string name="searching_for_game">正在搜索游戏...</string>
|
||||
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
<string name="add_directory_success">遊戲目錄新增成功</string>
|
||||
<string name="enable_update_checks">檢查更新</string>
|
||||
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
|
||||
<string name="update_available">發現新版本</string>
|
||||
<string name="update_available">偵測到新版本</string>
|
||||
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
|
||||
<string name="downloading_update">下載更新中</string>
|
||||
<string name="update_download_failed">更新下載失敗</string>
|
||||
@@ -1020,40 +1020,9 @@
|
||||
<string name="memory_6gb">6GB (不穩定)</string>
|
||||
<string name="memory_8gb">8GB (不穩定)</string>
|
||||
|
||||
<!-- CPU clock levels -->
|
||||
<string name="clock_normal">正常</string>
|
||||
<string name="clock_boost">加速</string>
|
||||
<string name="clock_fast">超頻</string>
|
||||
|
||||
<!-- GPU clock levels -->
|
||||
<string name="fast_gpu_normal">正常</string>
|
||||
<string name="fast_gpu_medium">加速</string>
|
||||
<string name="fast_gpu_high">超頻</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">極小(4MB)</string>
|
||||
<string name="gpu_swizzle_low">小(8MB)</string>
|
||||
<string name="gpu_swizzle_normal">正常(16MB)</string>
|
||||
<string name="gpu_swizzle_medium">中(32MB)</string>
|
||||
<string name="gpu_swizzle_high">大(64MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
|
||||
<string name="gpu_swizzlechunk_low">小(64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">正常(128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">中(256)</string>
|
||||
<string name="gpu_swizzlechunk_high">大(512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">攝氏</string>
|
||||
<string name="temperature_fahrenheit">華氏</string>
|
||||
<string name="temperature_celsius">攝氏度</string>
|
||||
<string name="temperature_fahrenheit">華氏度</string>
|
||||
|
||||
<!-- Memory Sizes -->
|
||||
<string name="memory_byte_shorthand">B</string>
|
||||
@@ -1066,8 +1035,6 @@
|
||||
|
||||
<string name="renderer_none">無</string>
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">效能</string>
|
||||
<string name="renderer_accuracy_high">準確</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -19,11 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
|
||||
}
|
||||
}
|
||||
|
||||
void ADSP::Shutdown() {
|
||||
audio_renderer->Stop();
|
||||
opus_decoder->Shutdown();
|
||||
}
|
||||
|
||||
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
|
||||
return *audio_renderer;
|
||||
}
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -45,7 +45,6 @@ public:
|
||||
explicit ADSP(Core::System& system, Sink::Sink& sink);
|
||||
~ADSP() = default;
|
||||
|
||||
void Shutdown();
|
||||
AudioRenderer::AudioRenderer& AudioRenderer();
|
||||
OpusDecoder::OpusDecoder& OpusDecoder();
|
||||
|
||||
|
||||
@@ -34,19 +34,26 @@ void AudioRenderer::Start() {
|
||||
|
||||
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
||||
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
|
||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||
"message response from ADSP!");
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
}
|
||||
|
||||
void AudioRenderer::Stop() {
|
||||
if (main_thread.joinable()) {
|
||||
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();
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
mailbox.Send(Direction::DSP, Message::Shutdown);
|
||||
if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
|
||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||
"message response from ADSP!");
|
||||
}
|
||||
main_thread.request_stop();
|
||||
main_thread.join();
|
||||
|
||||
for (auto& stream : streams) {
|
||||
if (stream) {
|
||||
stream->Stop();
|
||||
@@ -54,6 +61,7 @@ void AudioRenderer::Stop() {
|
||||
stream = nullptr;
|
||||
}
|
||||
}
|
||||
running = false;
|
||||
}
|
||||
|
||||
void AudioRenderer::Signal() {
|
||||
@@ -61,12 +69,13 @@ void AudioRenderer::Signal() {
|
||||
Send(Direction::DSP, Message::Render);
|
||||
}
|
||||
|
||||
void AudioRenderer::Wait(std::stop_token stop_token) {
|
||||
auto msg = mailbox.Receive(Direction::Host, stop_token);
|
||||
void AudioRenderer::Wait() {
|
||||
auto msg = Receive(Direction::Host);
|
||||
if (msg != Message::RenderResponse) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"Did not receive the expected render response from the AudioRenderer! Expected "
|
||||
"{}, got {}", Message::RenderResponse, msg);
|
||||
"Did not receive the expected render response from the AudioRenderer! Expected "
|
||||
"{}, got {}",
|
||||
Message::RenderResponse, msg);
|
||||
}
|
||||
PostDSPClearCommandBuffer();
|
||||
}
|
||||
@@ -138,9 +147,7 @@ void AudioRenderer::Main(std::stop_token stop_token) {
|
||||
constexpr u64 max_process_time{2'304'000ULL};
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
auto msg = mailbox.Receive(Direction::DSP, stop_token);
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
auto msg{mailbox.Receive(Direction::DSP)};
|
||||
switch (msg) {
|
||||
case Message::Shutdown:
|
||||
mailbox.Send(Direction::Host, Message::Shutdown);
|
||||
|
||||
@@ -63,15 +63,13 @@ public:
|
||||
*/
|
||||
void Start();
|
||||
|
||||
void NotifyShutdown();
|
||||
|
||||
/**
|
||||
* Stop the AudioRenderer.
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
void Signal();
|
||||
void Wait(std::stop_token stop_token);
|
||||
void Wait();
|
||||
|
||||
void Send(Direction dir, u32 message);
|
||||
u32 Receive(Direction dir);
|
||||
@@ -104,6 +102,8 @@ private:
|
||||
Mailbox mailbox;
|
||||
/// Main thread
|
||||
std::jthread main_thread{};
|
||||
/// The current state
|
||||
std::atomic<bool> running{};
|
||||
/// Shared memory of input command buffers, set by host, read by DSP
|
||||
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
|
||||
/// The command lists to process
|
||||
|
||||
@@ -41,16 +41,19 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
||||
}
|
||||
|
||||
OpusDecoder::~OpusDecoder() {
|
||||
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 {
|
||||
if (!running) {
|
||||
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) {
|
||||
@@ -70,6 +73,7 @@ void OpusDecoder::Init(std::stop_token stop_token) {
|
||||
return;
|
||||
}
|
||||
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
||||
running = true;
|
||||
Send(Direction::Host, Message::StartOK);
|
||||
}
|
||||
|
||||
@@ -78,15 +82,12 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
auto msg = Receive(Direction::DSP, stop_token);
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
|
||||
switch (msg) {
|
||||
case Message::Shutdown:
|
||||
case Shutdown:
|
||||
Send(Direction::Host, Message::ShutdownOK);
|
||||
return;
|
||||
|
||||
case Message::GetWorkBufferSize: {
|
||||
case GetWorkBufferSize: {
|
||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||
|
||||
ASSERT(IsValidChannelCount(channel_count));
|
||||
@@ -95,7 +96,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||
} break;
|
||||
|
||||
case Message::InitializeDecodeObject: {
|
||||
case InitializeDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||
@@ -112,7 +113,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case Message::ShutdownDecodeObject: {
|
||||
case ShutdownDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
|
||||
@@ -122,7 +123,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case Message::DecodeInterleaved: {
|
||||
case DecodeInterleaved: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
@@ -160,19 +161,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||
} break;
|
||||
|
||||
case Message::MapMemory: {
|
||||
case MapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::MapMemoryOK);
|
||||
} break;
|
||||
|
||||
case Message::UnmapMemory: {
|
||||
case UnmapMemory: {
|
||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||
} break;
|
||||
|
||||
case Message::GetWorkBufferSizeForMultiStream: {
|
||||
case GetWorkBufferSizeForMultiStream: {
|
||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
||||
|
||||
@@ -183,7 +184,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||
} break;
|
||||
|
||||
case Message::InitializeMultiStreamDecodeObject: {
|
||||
case InitializeMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
auto buffer_size = shared_memory->host_send_data[1];
|
||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||
@@ -210,7 +211,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case Message::ShutdownMultiStreamDecodeObject: {
|
||||
case ShutdownMultiStreamDecodeObject: {
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||
|
||||
@@ -220,7 +221,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||
} break;
|
||||
|
||||
case Message::DecodeInterleavedForMultiStream: {
|
||||
case DecodeInterleavedForMultiStream: {
|
||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||
|
||||
auto buffer = shared_memory->host_send_data[0];
|
||||
@@ -265,15 +266,4 @@ 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
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
~OpusDecoder();
|
||||
|
||||
bool IsRunning() const noexcept {
|
||||
return main_thread.joinable();
|
||||
return running;
|
||||
}
|
||||
|
||||
void Send(Direction dir, u32 message);
|
||||
@@ -67,8 +67,6 @@ public:
|
||||
shared_memory = &shared_memory_;
|
||||
}
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
||||
@@ -87,6 +85,8 @@ private:
|
||||
std::jthread init_thread{};
|
||||
/// 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,
|
||||
/// and the responses are written back by the OpusDecoder.
|
||||
SharedMemory* shared_memory{};
|
||||
|
||||
@@ -33,7 +33,6 @@ void AudioCore::CreateSinks() {
|
||||
|
||||
void AudioCore::Shutdown() {
|
||||
audio_manager->Shutdown();
|
||||
adsp->Shutdown();
|
||||
}
|
||||
|
||||
AudioManager& AudioCore::GetAudioManager() {
|
||||
|
||||
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
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)) {
|
||||
break;
|
||||
}
|
||||
@@ -35,9 +35,9 @@ AudioManager::AudioManager() {
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,9 @@ class AudioManager {
|
||||
public:
|
||||
explicit AudioManager();
|
||||
|
||||
/// @brief Shutdown the audio manager.
|
||||
/**
|
||||
* Shutdown the audio manager.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
|
||||
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
|
||||
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
|
||||
|
||||
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
|
||||
if (in_use == buffer_params->in_use && !buffer_unmapped) {
|
||||
error_info.error_code = ResultSuccess;
|
||||
error_info.address = CpuAddr(0);
|
||||
out_status.writeOffset = current_state->last_pos2;
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -31,10 +31,10 @@ public:
|
||||
};
|
||||
|
||||
struct DeviceInParameter {
|
||||
/* 0x000 */ u8 name[0x100];
|
||||
/* 0x000 */ char name[0x100];
|
||||
/* 0x100 */ u32 input_count;
|
||||
/* 0x104 */ std::array<s8, MaxChannels> inputs;
|
||||
/* 0x10A */ u8 unk10A[0x1];
|
||||
/* 0x10A */ char unk10A[0x1];
|
||||
/* 0x10B */ bool downmix_enabled;
|
||||
/* 0x10C */ std::array<f32, 4> downmix_coeff;
|
||||
};
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
struct DeviceState {
|
||||
/* 0x00 */ UpsamplerInfo* upsampler_info;
|
||||
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
|
||||
/* 0x18 */ u8 unk18[0x18];
|
||||
/* 0x18 */ char unk18[0x18];
|
||||
};
|
||||
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
|
||||
|
||||
@@ -55,8 +55,8 @@ public:
|
||||
/* 0x14 */ u32 previous_pos;
|
||||
/* 0x18 */ SampleFormat format;
|
||||
/* 0x1C */ std::array<s8, MaxChannels> inputs;
|
||||
/* 0x22 */ u8 in_use;
|
||||
/* 0x23 */ u8 unk23[0x5];
|
||||
/* 0x22 */ bool in_use;
|
||||
/* 0x23 */ char unk23[0x5];
|
||||
};
|
||||
static_assert(sizeof(CircularBufferInParameter) == 0x28,
|
||||
"CircularBufferInParameter has the wrong size!");
|
||||
@@ -65,16 +65,16 @@ public:
|
||||
/* 0x00 */ u32 last_pos2;
|
||||
/* 0x04 */ s32 current_pos;
|
||||
/* 0x08 */ u32 last_pos;
|
||||
/* 0x0C */ u8 unk0C[0x4];
|
||||
/* 0x0C */ char unk0C[0x4];
|
||||
/* 0x10 */ AddressInfo address_info;
|
||||
};
|
||||
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
|
||||
|
||||
struct InParameter {
|
||||
/* 0x000 */ Type type;
|
||||
/* 0x001 */ u8 in_use;
|
||||
/* 0x001 */ bool in_use;
|
||||
/* 0x004 */ u32 node_id;
|
||||
/* 0x008 */ u8 unk08[0x18];
|
||||
/* 0x008 */ char unk08[0x18];
|
||||
union {
|
||||
/* 0x020 */ DeviceInParameter device;
|
||||
/* 0x020 */ CircularBufferInParameter circular_buffer;
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
|
||||
struct OutStatus {
|
||||
/* 0x00 */ u32 writeOffset;
|
||||
/* 0x04 */ u8 unk04[0x1C];
|
||||
/* 0x04 */ char unk04[0x1C];
|
||||
}; // size == 0x20
|
||||
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
|
||||
|
||||
@@ -162,18 +162,19 @@ public:
|
||||
u8* GetParameter();
|
||||
|
||||
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
|
||||
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
|
||||
/// Parameter buffer for this sink
|
||||
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> 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;
|
||||
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
|
||||
parameter{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::Renderer
|
||||
|
||||
@@ -100,9 +100,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
|
||||
}
|
||||
|
||||
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||
: core{core_}
|
||||
, adsp_rendered_event{adsp_rendered_event_}
|
||||
{}
|
||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
|
||||
adsp_rendered_event{adsp_rendered_event_} {}
|
||||
|
||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
@@ -407,7 +406,7 @@ void System::Finalize() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsActive()) {
|
||||
if (active) {
|
||||
Stop();
|
||||
}
|
||||
|
||||
@@ -434,12 +433,14 @@ void System::Start() {
|
||||
std::scoped_lock l{lock};
|
||||
frames_elapsed = 0;
|
||||
state = State::Started;
|
||||
active = true;
|
||||
}
|
||||
|
||||
void System::Stop() {
|
||||
{
|
||||
std::scoped_lock l{lock};
|
||||
state = State::Stopped;
|
||||
active = false;
|
||||
}
|
||||
|
||||
if (execution_mode == ExecutionMode::Auto) {
|
||||
@@ -479,7 +480,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
||||
return result;
|
||||
}
|
||||
|
||||
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer,
|
||||
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
|
||||
memory_pool_count);
|
||||
if (result.IsError()) {
|
||||
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
||||
@@ -581,16 +582,16 @@ u32 System::GetRenderingDevice() const {
|
||||
}
|
||||
|
||||
bool System::IsActive() const {
|
||||
return state == State::Started;
|
||||
return active;
|
||||
}
|
||||
|
||||
void System::SendCommandToDsp() {
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
if (initialized) {
|
||||
if (IsActive()) {
|
||||
if (active) {
|
||||
terminate_event.Reset();
|
||||
const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id);
|
||||
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
|
||||
u64 command_size{0};
|
||||
|
||||
if (remaining_command_count) {
|
||||
@@ -617,17 +618,16 @@ void System::SendCommandToDsp() {
|
||||
auto time_limit{
|
||||
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
||||
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
||||
core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id,
|
||||
translated_addr, command_size,
|
||||
time_limit, applet_resource_user_id,
|
||||
process_handle, reset_command_buffers);
|
||||
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
|
||||
applet_resource_user_id, process_handle,
|
||||
reset_command_buffers);
|
||||
reset_command_buffers = false;
|
||||
command_buffer_size = command_size;
|
||||
if (remaining_command_count == 0) {
|
||||
adsp_rendered_event->Signal(core.Kernel());
|
||||
}
|
||||
} else {
|
||||
core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id);
|
||||
audio_renderer.ClearRemainCommandCount(session_id);
|
||||
terminate_event.Set();
|
||||
}
|
||||
}
|
||||
@@ -738,7 +738,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
||||
total_ticks_elapsed += end_time - start_time;
|
||||
num_command_lists_generated++;
|
||||
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
|
||||
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
|
||||
frames_elapsed++;
|
||||
|
||||
return command_buffer.size;
|
||||
|
||||
@@ -219,6 +219,8 @@ public:
|
||||
private:
|
||||
/// Core system
|
||||
Core::System& core;
|
||||
/// Reference to the ADSP's AudioRenderer for communication
|
||||
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
||||
/// Is this system initialized?
|
||||
bool initialized{};
|
||||
/// Is this system currently active?
|
||||
|
||||
@@ -11,70 +11,73 @@
|
||||
#include "audio_core/renderer/system_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
|
||||
SystemManager::SystemManager(Core::System& core_)
|
||||
: core{core_}
|
||||
{
|
||||
}
|
||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
|
||||
|
||||
SystemManager::~SystemManager() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void SystemManager::InitializeUnsafe() {
|
||||
if (!thread.joinable()) {
|
||||
core.AudioCore().ADSP().AudioRenderer().Start();
|
||||
thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() {
|
||||
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||
auto const stop_token = thread.get_stop_token();
|
||||
while (!stop_token.stop_requested()) {
|
||||
if (!active) {
|
||||
active = true;
|
||||
audio_renderer.Start();
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
while (active && !stop_token.stop_requested()) {
|
||||
{
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock l{mutex1};
|
||||
for (auto system : systems)
|
||||
system->SendCommandToDsp();
|
||||
}
|
||||
audio_renderer.Signal();
|
||||
audio_renderer.Wait(stop_token);
|
||||
audio_renderer.Wait();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SystemManager::Stop() {
|
||||
core.AudioCore().ADSP().AudioRenderer().Stop();
|
||||
if (thread.joinable()) {
|
||||
if (active) {
|
||||
active = false;
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
audio_renderer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemManager::Add(System& system_) {
|
||||
std::scoped_lock lk{mutex};
|
||||
if (systems.size() >= MaxRendererSessions) {
|
||||
std::scoped_lock l2{mutex2};
|
||||
if (systems.size() + 1 > MaxRendererSessions) {
|
||||
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
||||
return false;
|
||||
}
|
||||
systems.push_back(&system_);
|
||||
if (!systems.empty()) {
|
||||
InitializeUnsafe();
|
||||
{
|
||||
std::scoped_lock l{mutex1};
|
||||
if (systems.empty())
|
||||
InitializeUnsafe();
|
||||
}
|
||||
systems.push_back(&system_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemManager::Remove(System& system_) {
|
||||
std::scoped_lock lk{mutex};
|
||||
if (systems.remove(&system_) == 0) {
|
||||
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
||||
return false;
|
||||
std::scoped_lock l2{mutex2};
|
||||
{
|
||||
std::scoped_lock l{mutex1};
|
||||
if (systems.remove(&system_) == 0) {
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,14 @@ private:
|
||||
std::list<System*> systems{};
|
||||
/// Main worker thread for generating command lists
|
||||
std::jthread thread;
|
||||
/// Mutex for the reading and adding/removing systems
|
||||
std::mutex mutex{};
|
||||
/// Mutex for the systems
|
||||
std::mutex mutex1{};
|
||||
/// 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
|
||||
|
||||
@@ -109,6 +109,12 @@ private:
|
||||
if (read_index == producer.index.load(std::memory_order::acquire)) {
|
||||
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) {
|
||||
// Wait until the queue is not empty.
|
||||
std::unique_lock lock{consumer.cv_mutex};
|
||||
@@ -118,12 +124,6 @@ private:
|
||||
if (stop_token.stop_requested()) {
|
||||
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 {
|
||||
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
||||
}
|
||||
|
||||
@@ -244,8 +244,7 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
||||
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
||||
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
||||
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
||||
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
|
||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
|
||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
|
||||
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
||||
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
||||
, invariant{invariant_}
|
||||
@@ -292,7 +291,7 @@ bool WallClock::IsNative() const {
|
||||
}
|
||||
|
||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
|
||||
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
|
||||
}
|
||||
#elif defined(HAS_NCE)
|
||||
namespace {
|
||||
@@ -417,7 +416,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||
const WallClock g_wall_clock = [] {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
auto const& caps = Common::g_cpu_caps;
|
||||
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
|
||||
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
|
||||
#elif defined(HAS_NCE)
|
||||
return WallClock(false, 1);
|
||||
#else
|
||||
|
||||
@@ -96,7 +96,6 @@ public:
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 rdtsc_ns_integer;
|
||||
u64 rdtsc_ns_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
|
||||
+20
-18
@@ -17,33 +17,35 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
|
||||
// We don't need to use the preprocessor, we can just select depending on return type
|
||||
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
|
||||
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
|
||||
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
|
||||
template<> std::string HandleStrerrorR(int r, char *err_str) {
|
||||
return std::string{r != 0
|
||||
? "(strerror_r failed to format error)"
|
||||
: err_str};
|
||||
}
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
LPSTR(&err_str), 1, nullptr);
|
||||
if (res) {
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
}
|
||||
return "(FormatMessageA failed to format error)";
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
#else
|
||||
char err_str[255];
|
||||
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||
#if defined(__ANDROID__) || \
|
||||
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
@@ -649,10 +649,10 @@ struct Values {
|
||||
|
||||
SwitchableSetting<bool> fix_bloom_effects{linkage, false, "fix_bloom_effects",
|
||||
Category::RendererHacks};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
|
||||
Category::RendererHacks};
|
||||
|
||||
#endif
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
|
||||
@@ -22,15 +22,21 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
|
||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||
{}
|
||||
|
||||
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read8(vaddr);
|
||||
}
|
||||
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read16(vaddr);
|
||||
}
|
||||
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
@@ -44,17 +50,27 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Write64(vaddr, value);
|
||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write8(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write64(vaddr, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
m_memory.WriteExclusive8(vaddr, value, expected);
|
||||
|
||||
@@ -30,12 +30,18 @@ class System;
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
||||
u64 MemoryRead(u32 vaddr, size_t size) override;
|
||||
u8 MemoryRead8(u32 vaddr) override;
|
||||
u16 MemoryRead16(u32 vaddr) override;
|
||||
u32 MemoryRead32(u32 vaddr) override;
|
||||
u64 MemoryRead64(u32 vaddr) override;
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override;
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override;
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override;
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -22,15 +21,21 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
|
||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||
{}
|
||||
|
||||
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read8(vaddr);
|
||||
}
|
||||
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read16(vaddr);
|
||||
}
|
||||
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
}
|
||||
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
@@ -48,15 +53,24 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
|
||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write8(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write64(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
@@ -37,13 +36,19 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||
|
||||
u64 MemoryRead(u64 vaddr, size_t size) override;
|
||||
u8 MemoryRead8(u64 vaddr) override;
|
||||
u16 MemoryRead16(u64 vaddr) override;
|
||||
u32 MemoryRead32(u64 vaddr) override;
|
||||
u64 MemoryRead64(u64 vaddr) override;
|
||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override;
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override;
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override;
|
||||
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
|
||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
|
||||
|
||||
+23
-45
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}
|
||||
, fs_controller{system}
|
||||
, hid_core{kernel}
|
||||
, cpu_manager{system}
|
||||
, reporter{system}
|
||||
, profile_manager{}
|
||||
{}
|
||||
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
|
||||
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
|
||||
|
||||
u64 program_id;
|
||||
|
||||
@@ -129,12 +124,6 @@ struct System::Impl {
|
||||
core_timing.SetMulticore(is_multicore);
|
||||
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.
|
||||
if (virtual_filesystem == nullptr) {
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
@@ -144,7 +133,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
@@ -387,7 +376,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// 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_current_game) {
|
||||
@@ -439,27 +428,13 @@ struct System::Impl {
|
||||
core_timing.SyncPause(false);
|
||||
Network::CancelPendingSocketOperations();
|
||||
kernel.SuspendEmulation(true);
|
||||
kernel.ShutdownCores();
|
||||
|
||||
// Notify services helpers of shutdown
|
||||
audio_core->Shutdown();
|
||||
|
||||
// Wait for threads/services to join
|
||||
kernel.CloseServices();
|
||||
|
||||
// service shutdown
|
||||
kernel.ShutdownCores();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
frontend_applets.reset();
|
||||
applet_manager.reset();
|
||||
apm_controller.reset();
|
||||
arp_manager.reset();
|
||||
profile_manager.reset();
|
||||
|
||||
fs_controller.Reset();
|
||||
cheat_engine.reset();
|
||||
core_timing.ClearPendingEvents();
|
||||
core_timing.Reset();
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
gpu_core.reset();
|
||||
@@ -476,6 +451,9 @@ struct System::Impl {
|
||||
room_member->SendGameInfo(game_info);
|
||||
}
|
||||
|
||||
// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
@@ -504,13 +482,13 @@ struct System::Impl {
|
||||
CpuManager cpu_manager;
|
||||
Reporter reporter;
|
||||
/// Applets
|
||||
std::optional<Service::AM::AppletManager> applet_manager;
|
||||
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets;
|
||||
Service::AM::AppletManager applet_manager;
|
||||
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
|
||||
/// APM (Performance) services
|
||||
std::optional<Service::APM::Controller> apm_controller;
|
||||
Service::APM::Controller apm_controller{core_timing};
|
||||
/// Service State
|
||||
std::optional<Service::Glue::ARPManager> arp_manager;
|
||||
std::optional<Service::Account::ProfileManager> profile_manager;
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::Account::ProfileManager profile_manager;
|
||||
/// Network instance
|
||||
Network::NetworkInstance network_instance;
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
@@ -837,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
}
|
||||
|
||||
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() {
|
||||
return *impl->frontend_applets;
|
||||
return impl->frontend_applets;
|
||||
}
|
||||
|
||||
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
|
||||
return *impl->frontend_applets;
|
||||
return impl->frontend_applets;
|
||||
}
|
||||
|
||||
Service::AM::AppletManager& System::GetAppletManager() {
|
||||
return *impl->applet_manager;
|
||||
return impl->applet_manager;
|
||||
}
|
||||
|
||||
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
||||
@@ -890,27 +868,27 @@ const Reporter& System::GetReporter() const {
|
||||
}
|
||||
|
||||
Service::Glue::ARPManager& System::GetARPManager() {
|
||||
return *impl->arp_manager;
|
||||
return impl->arp_manager;
|
||||
}
|
||||
|
||||
const Service::Glue::ARPManager& System::GetARPManager() const {
|
||||
return *impl->arp_manager;
|
||||
return impl->arp_manager;
|
||||
}
|
||||
|
||||
Service::APM::Controller& System::GetAPMController() {
|
||||
return *impl->apm_controller;
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
const Service::APM::Controller& System::GetAPMController() const {
|
||||
return *impl->apm_controller;
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
Service::Account::ProfileManager& System::GetProfileManager() {
|
||||
return *impl->profile_manager;
|
||||
return impl->profile_manager;
|
||||
}
|
||||
|
||||
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||
return *impl->profile_manager;
|
||||
return impl->profile_manager;
|
||||
}
|
||||
|
||||
void System::SetExitLocked(bool locked) {
|
||||
|
||||
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
|
||||
void CoreTiming::Reset() {
|
||||
paused = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread.joinable()) {
|
||||
timer_thread.request_stop();
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
timer_thread.join();
|
||||
}
|
||||
has_started = false;
|
||||
|
||||
@@ -35,20 +35,17 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
||||
}
|
||||
|
||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
}
|
||||
|
||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
if (in == nullptr || ips == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||
if (type == IPSFileType::Error)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
if (in_data.size() == 0)
|
||||
auto const type = IdentifyMagic(in_data);
|
||||
if (type == IPSFileType::Error)
|
||||
return nullptr;
|
||||
|
||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
@@ -157,15 +156,12 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
||||
NACP nacp{nacp_file};
|
||||
return nacp.GetVersionString();
|
||||
}
|
||||
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
|
||||
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
PatchManager::PatchManager(u64 title_id_,
|
||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||
const ContentProvider& content_provider_)
|
||||
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
|
||||
PatchManager::~PatchManager() = default;
|
||||
|
||||
@@ -173,35 +169,13 @@ u64 PatchManager::GetTitleID() const {
|
||||
return title_id;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
|
||||
const auto get_root = [&](u64 id) {
|
||||
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
|
||||
};
|
||||
auto root = get_root(title_id);
|
||||
if (!parent_title_id) {
|
||||
return root;
|
||||
}
|
||||
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
|
||||
std::erase(roots, nullptr);
|
||||
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
|
||||
}
|
||||
|
||||
std::vector<std::string> PatchManager::GetDisabledAddons() const {
|
||||
auto disabled = Settings::values.disabled_addons[title_id];
|
||||
if (parent_title_id) {
|
||||
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
|
||||
disabled.insert(disabled.end(), shared.begin(), shared.end());
|
||||
}
|
||||
return disabled;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -329,8 +303,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
// LayeredExeFS
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
|
||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||
if (load_dir != nullptr) {
|
||||
@@ -372,7 +346,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||
|
||||
std::vector<VirtualFile> out;
|
||||
@@ -431,7 +405,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return nso;
|
||||
@@ -474,7 +448,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return false;
|
||||
@@ -488,13 +462,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
}
|
||||
|
||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
@@ -528,16 +502,17 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
||||
return out;
|
||||
}
|
||||
|
||||
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||
type != ContentRecordType::HtmlDocument) ||
|
||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||
patch_dirs.push_back(sdmc_load_dir);
|
||||
@@ -616,7 +591,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -723,7 +698,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
|
||||
// LayeredFS
|
||||
if (apply_layeredfs) {
|
||||
ApplyLayeredFS(romfs, type);
|
||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
||||
}
|
||||
|
||||
return romfs;
|
||||
@@ -1097,22 +1072,15 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto version = content_provider.GetEntryVersion(update_tid);
|
||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
|
||||
return content_provider.GetEntryVersion(update_tid);
|
||||
}
|
||||
|
||||
const auto version = content_provider.GetEntryVersion(title_id);
|
||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
|
||||
return content_provider.GetEntryVersion(title_id);
|
||||
}
|
||||
|
||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||
if (base_control_nca == nullptr) {
|
||||
if (parent_title_id) {
|
||||
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
|
||||
const PatchManager parent{control_id, fs_controller, content_provider};
|
||||
return parent.GetControlMetadata();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -109,14 +109,10 @@ public:
|
||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
|
||||
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
|
||||
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
|
||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const;
|
||||
|
||||
u64 title_id;
|
||||
std::optional<u64> parent_title_id;
|
||||
const Service::FileSystem::FileSystemController& fs_controller;
|
||||
const ContentProvider& content_provider;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <openssl/evp.h>
|
||||
@@ -349,22 +348,6 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
|
||||
const auto application_id = GetBaseTitleID(program_id);
|
||||
const auto program_index = program_id - application_id;
|
||||
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|
||||
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
|
||||
return application_id;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||
|
||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||
|
||||
@@ -100,8 +100,6 @@ public:
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const = 0;
|
||||
|
||||
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
|
||||
|
||||
protected:
|
||||
// A single instance of KeyManager to be used by GetEntry()
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
|
||||
|
||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
||||
ASSERT(this->IsLocked());
|
||||
for (auto* thread : m_woken_dummy_threads)
|
||||
thread->DummyThreadEndWait(kernel);
|
||||
m_woken_dummy_threads.clear();
|
||||
if (m_woken_dummy_threads.size() > 0) {
|
||||
for (auto* thread : m_woken_dummy_threads)
|
||||
thread->DummyThreadEndWait(kernel);
|
||||
m_woken_dummy_threads.clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
|
||||
KThread* cur_child = nullptr;
|
||||
{
|
||||
KScopedLightLock proc_lk(process->GetListLock());
|
||||
|
||||
auto& thread_list = process->GetThreadList();
|
||||
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
|
||||
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
|
||||
|
||||
@@ -1236,13 +1236,18 @@ namespace Kernel {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Determine if this is the first termination request.
|
||||
// Perform an atomic compare-and-swap from false to true.
|
||||
bool expected = false;
|
||||
const bool first_request = [&]() -> bool {
|
||||
// Perform an atomic compare-and-swap from false to true.
|
||||
bool expected = false;
|
||||
return m_termination_requested.compare_exchange_strong(expected, true);
|
||||
}();
|
||||
|
||||
// If this is the first request, start termination procedure.
|
||||
if (m_termination_requested.compare_exchange_strong(expected, true)) {
|
||||
if (first_request) {
|
||||
// If the thread is in initialized state, just change state to terminated.
|
||||
if (this->GetState() == ThreadState::Initialized) {
|
||||
return m_thread_state = ThreadState::Terminated;
|
||||
m_thread_state = ThreadState::Terminated;
|
||||
return ThreadState::Terminated;
|
||||
}
|
||||
|
||||
// Register the terminating dpc.
|
||||
@@ -1276,6 +1281,7 @@ namespace Kernel {
|
||||
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
|
||||
}
|
||||
}
|
||||
|
||||
return this->GetState();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
@@ -28,33 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
|
||||
|
||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
||||
ASSERT(type <= WorkerType::Count);
|
||||
@@ -63,11 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
|
||||
|
||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
{
|
||||
std::scoped_lock lk{m_task_mutex};
|
||||
m_task_queue.emplace_back(task);
|
||||
}
|
||||
m_task_cv.notify_one();
|
||||
m_waiting_thread.QueueWork([&kernel, task]() {
|
||||
// Do the task.
|
||||
task->DoWorkerTask(kernel);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_worker.h"
|
||||
|
||||
@@ -23,17 +18,15 @@ public:
|
||||
Count,
|
||||
};
|
||||
|
||||
KWorkerTaskManager(KernelCore& kernel);
|
||||
~KWorkerTaskManager();
|
||||
KWorkerTaskManager();
|
||||
|
||||
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
|
||||
|
||||
private:
|
||||
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
||||
std::jthread m_waiting_thread;
|
||||
|
||||
std::mutex m_task_mutex;
|
||||
std::condition_variable_any m_task_cv;
|
||||
std::vector<KWorkerTask*> m_task_queue;
|
||||
private:
|
||||
Common::ThreadWorker m_waiting_thread;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -80,10 +80,7 @@ struct KernelCore::Impl {
|
||||
// so it will be statically given a TLS slot anyways.
|
||||
static inline thread_local ThreadLocalData tls_data = {};
|
||||
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
||||
: worker_task_manager{kernel_}
|
||||
, system{system_}
|
||||
{
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
|
||||
tls_data.lock = true;
|
||||
}
|
||||
|
||||
@@ -970,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
|
||||
return *impl->hardware_timer;
|
||||
}
|
||||
|
||||
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
|
||||
if (!impl->global_object_list_container)
|
||||
return nullptr;
|
||||
return std::addressof(*impl->global_object_list_container);
|
||||
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
|
||||
return *impl->global_object_list_container;
|
||||
}
|
||||
|
||||
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
|
||||
if (!impl->global_object_list_container)
|
||||
return nullptr;
|
||||
return std::addressof(*impl->global_object_list_container);
|
||||
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
|
||||
return *impl->global_object_list_container;
|
||||
}
|
||||
|
||||
void KernelCore::PrepareReschedule(std::size_t id) {
|
||||
@@ -1008,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
||||
|
||||
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
||||
auto* manager = server_manager.get();
|
||||
if (!impl->is_shutting_down) {
|
||||
|
||||
{
|
||||
std::scoped_lock lk{impl->server_lock};
|
||||
if (impl->is_shutting_down) {
|
||||
return;
|
||||
}
|
||||
|
||||
impl->server_managers.emplace_back(std::move(server_manager));
|
||||
}
|
||||
|
||||
manager->LoopProcess();
|
||||
}
|
||||
|
||||
@@ -1256,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
|
||||
}
|
||||
|
||||
void KernelCore::ShutdownCores() {
|
||||
// Notify shutdown (pre-emptively)
|
||||
for (auto& sm : impl->server_managers) {
|
||||
sm->NotifyShutdown();
|
||||
}
|
||||
impl->TerminateAllProcesses();
|
||||
KScopedSchedulerLock lk{*this};
|
||||
for (auto* thread : impl->shutdown_threads)
|
||||
|
||||
@@ -175,8 +175,9 @@ public:
|
||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||
void PrepareReschedule(std::size_t id);
|
||||
|
||||
KAutoObjectWithListContainer* ObjectListContainer();
|
||||
const KAutoObjectWithListContainer* ObjectListContainer() const;
|
||||
KAutoObjectWithListContainer& ObjectListContainer();
|
||||
|
||||
const KAutoObjectWithListContainer& ObjectListContainer() const;
|
||||
|
||||
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
||||
/// leaks after emulation has been shutdown.
|
||||
|
||||
@@ -151,8 +151,7 @@ public:
|
||||
const bool is_initialized = this->IsInitialized();
|
||||
uintptr_t arg = 0;
|
||||
if (is_initialized) {
|
||||
if (auto const olc = kernel.ObjectListContainer(); olc)
|
||||
olc->Unregister(this);
|
||||
kernel.ObjectListContainer().Unregister(this);
|
||||
arg = this->GetPostDestroyArgument();
|
||||
this->Finalize(kernel);
|
||||
}
|
||||
@@ -176,7 +175,7 @@ public:
|
||||
public:
|
||||
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
|
||||
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
|
||||
kernel.ObjectListContainer()->Initialize();
|
||||
kernel.ObjectListContainer().Initialize();
|
||||
}
|
||||
|
||||
static Derived* Create(KernelCore& kernel) {
|
||||
@@ -187,7 +186,7 @@ public:
|
||||
}
|
||||
|
||||
static void Register(KernelCore& kernel, Derived* obj) {
|
||||
return kernel.ObjectListContainer()->Register(obj);
|
||||
return kernel.ObjectListContainer().Register(obj);
|
||||
}
|
||||
|
||||
static size_t GetObjectSize(KernelCore& kernel) {
|
||||
|
||||
@@ -15,9 +15,13 @@ void LoopProcess(Core::System& system) {
|
||||
WindowSystem window_system(system);
|
||||
ButtonPoller button_poller(system, window_system);
|
||||
EventObserver event_observer(system, window_system);
|
||||
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
|
||||
server_manager->RegisterNamedService("appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"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));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,25 +26,24 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||
system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() {
|
||||
auto const stop_token = m_stop_source.get_token();
|
||||
m_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
while (!stop_token.stop_requested()) {
|
||||
auto* signaled_holder = this->WaitSignaled(stop_token);
|
||||
if (stop_token.stop_requested() || !signaled_holder)
|
||||
if (!signaled_holder)
|
||||
break;
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
m_process_cv.notify_one();
|
||||
});
|
||||
}
|
||||
|
||||
EventObserver::~EventObserver() {
|
||||
// Signal thread and wait for processing to finish.
|
||||
m_stop_source.request_stop();
|
||||
m_wakeup_event.Signal(m_system.Kernel());
|
||||
{
|
||||
std::unique_lock lk{m_process_mutex};
|
||||
m_process_cv.wait(lk);
|
||||
if (m_thread.joinable()) {
|
||||
// Signal thread and wait for processing to finish.
|
||||
m_thread.request_stop();
|
||||
m_wakeup_event.Signal(m_system.Kernel());
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
// Free remaining owned sessions.
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <stop_token>
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
@@ -64,9 +62,7 @@ private:
|
||||
MultiWait m_deferred_wait_list;
|
||||
|
||||
// Processing thread.
|
||||
std::stop_source m_stop_source{};
|
||||
std::mutex m_process_mutex;
|
||||
std::condition_variable m_process_cv{};
|
||||
std::jthread m_thread{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::AM::Frontend {
|
||||
@@ -73,16 +72,10 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
|
||||
|
||||
void FrontendApplet::Exit() {
|
||||
auto applet_ = applet.lock();
|
||||
{
|
||||
std::scoped_lock lk{applet_->lock};
|
||||
applet_->is_completed = true;
|
||||
applet_->state_changed_event.Signal(system.Kernel());
|
||||
}
|
||||
if (auto caller_applet = applet_->caller_applet.lock()) {
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
std::erase(caller_applet->child_applets, applet_);
|
||||
}
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
|
||||
|
||||
std::scoped_lock lk{applet_->lock};
|
||||
applet_->is_completed = true;
|
||||
applet_->state_changed_event.Signal(system.Kernel());
|
||||
}
|
||||
|
||||
FrontendAppletSet::FrontendAppletSet() = default;
|
||||
|
||||
@@ -92,13 +92,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
|
||||
if (m_focus_state_changed_notification_enabled) {
|
||||
m_has_focus_state_changed = true;
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
|
||||
|
||||
public:
|
||||
|
||||
@@ -164,15 +164,15 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
||||
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
|
||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
|
||||
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
|
||||
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
|
||||
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
|
||||
caller_applet->lifecycle_manager.RequestResumeNotification();
|
||||
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
|
||||
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||
}
|
||||
|
||||
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
|
||||
|
||||
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
|
||||
auto impl = (*out_storage)->GetImpl();
|
||||
|
||||
|
||||
@@ -122,10 +122,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
{
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
}
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
window_system.TrackApplet(applet, false);
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
@@ -151,10 +148,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = storage;
|
||||
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
||||
{
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
}
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
|
||||
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
||||
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
||||
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
|
||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
|
||||
|
||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
||||
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
|
||||
}
|
||||
|
||||
TimeManager::TimeManager(Core::System& system)
|
||||
: m_steady_clock_resource{system}, m_time_zone_binary{system}
|
||||
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker}
|
||||
{
|
||||
: m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
|
||||
system,
|
||||
m_steady_clock_resource,
|
||||
m_file_timestamp_worker} {
|
||||
m_time_m =
|
||||
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
||||
|
||||
|
||||
@@ -20,17 +20,15 @@
|
||||
|
||||
namespace Service::Glue::Time {
|
||||
|
||||
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker)
|
||||
: m_system{system}
|
||||
, m_ctx{m_system, "Glue:TimeWorker"}
|
||||
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")}
|
||||
, m_steady_clock_resource{steady_clock_resource}
|
||||
, m_file_timestamp_worker{file_timestamp_worker}
|
||||
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")}
|
||||
, 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}
|
||||
{
|
||||
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
||||
FileTimestampWorker& file_timestamp_worker)
|
||||
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
|
||||
"Glue:TimeWorker:Event")},
|
||||
m_steady_clock_resource{steady_clock_resource},
|
||||
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
|
||||
"Glue:TimeWorker:SteadyClockTimerEvent")},
|
||||
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_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
||||
"Time::SteadyClockEvent",
|
||||
[this](s64 time,
|
||||
@@ -49,16 +47,14 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
|
||||
}
|
||||
|
||||
TimeWorker::~TimeWorker() {
|
||||
// Wait for processing to stop
|
||||
m_stop_source.request_stop();
|
||||
m_local_clock_event->Signal(m_system.Kernel());
|
||||
m_network_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());
|
||||
{
|
||||
std::unique_lock lk{m_process_mutex};
|
||||
m_process_cv.wait(lk);
|
||||
}
|
||||
m_thread.join();
|
||||
|
||||
m_ctx.CloseEvent(m_event);
|
||||
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
|
||||
@@ -126,162 +122,166 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
|
||||
}
|
||||
|
||||
void TimeWorker::StartThread() {
|
||||
m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() {
|
||||
auto const stop_token = m_stop_source.get_token();
|
||||
while (!stop_token.stop_requested()) {
|
||||
enum class EventType : s32 {
|
||||
Exit = 0,
|
||||
PowerStateChange = 1,
|
||||
SignalAlarms = 2,
|
||||
UpdateLocalSystemClock = 3,
|
||||
UpdateNetworkSystemClock = 4,
|
||||
UpdateEphemeralSystemClock = 5,
|
||||
UpdateSteadyClock = 6,
|
||||
UpdateFileTimestamp = 7,
|
||||
AutoCorrect = 8,
|
||||
};
|
||||
s32 index{};
|
||||
if (m_pm_state_change_handler.m_priority != 0) {
|
||||
// TODO: gIPmModuleService::GetEvent() 1
|
||||
index = WaitAny(
|
||||
m_system.Kernel(),
|
||||
&m_event->GetReadableEvent(), // 0
|
||||
&m_alarm_worker.GetEvent() // 1
|
||||
);
|
||||
} else {
|
||||
// TODO: gIPmModuleService::GetEvent() 1
|
||||
index = WaitAny(
|
||||
m_system.Kernel(),
|
||||
&m_event->GetReadableEvent(), // 0
|
||||
&m_alarm_worker.GetEvent(), // 1
|
||||
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
|
||||
m_local_clock_event, // 3
|
||||
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
|
||||
);
|
||||
}
|
||||
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
|
||||
}
|
||||
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("TimeWorker");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
|
||||
|
||||
switch (EventType(index)) {
|
||||
case EventType::Exit:
|
||||
return;
|
||||
while (!stop_token.stop_requested()) {
|
||||
enum class EventType : s32 {
|
||||
Exit = 0,
|
||||
PowerStateChange = 1,
|
||||
SignalAlarms = 2,
|
||||
UpdateLocalSystemClock = 3,
|
||||
UpdateNetworkSystemClock = 4,
|
||||
UpdateEphemeralSystemClock = 5,
|
||||
UpdateSteadyClock = 6,
|
||||
UpdateFileTimestamp = 7,
|
||||
AutoCorrect = 8,
|
||||
};
|
||||
|
||||
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;
|
||||
s32 index{};
|
||||
|
||||
case EventType::SignalAlarms:
|
||||
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
|
||||
m_time_m->CheckAndSignalAlarms();
|
||||
break;
|
||||
|
||||
case EventType::UpdateLocalSystemClock: {
|
||||
m_local_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
|
||||
|
||||
m_set_sys->SetUserSystemClockContext(context);
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::UpdateNetworkSystemClock: {
|
||||
m_network_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
|
||||
|
||||
m_set_sys->SetNetworkSystemClockContext(context);
|
||||
|
||||
s64 time{};
|
||||
if (m_network_clock->GetCurrentTime(&time) != 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;
|
||||
}
|
||||
|
||||
case EventType::UpdateEphemeralSystemClock: {
|
||||
m_ephemeral_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
if (m_pm_state_change_handler.m_priority != 0) {
|
||||
// TODO: gIPmModuleService::GetEvent() 1
|
||||
index = WaitAny(m_system.Kernel(),
|
||||
&m_event->GetReadableEvent(), // 0
|
||||
&m_alarm_worker.GetEvent() // 1
|
||||
);
|
||||
} else {
|
||||
// TODO: gIPmModuleService::GetEvent() 1
|
||||
index = WaitAny(m_system.Kernel(),
|
||||
&m_event->GetReadableEvent(), // 0
|
||||
&m_alarm_worker.GetEvent(), // 1
|
||||
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
|
||||
m_local_clock_event, // 3
|
||||
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
|
||||
);
|
||||
}
|
||||
m_process_cv.notify_one();
|
||||
});
|
||||
|
||||
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:
|
||||
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
|
||||
m_time_m->CheckAndSignalAlarms();
|
||||
break;
|
||||
|
||||
case EventType::UpdateLocalSystemClock: {
|
||||
m_local_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
|
||||
|
||||
m_set_sys->SetUserSystemClockContext(context);
|
||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::UpdateNetworkSystemClock: {
|
||||
m_network_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
Service::PSC::Time::SystemClockContext context{};
|
||||
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
|
||||
|
||||
m_set_sys->SetNetworkSystemClockContext(context);
|
||||
|
||||
s64 time{};
|
||||
if (m_network_clock->GetCurrentTime(&time) != 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;
|
||||
}
|
||||
|
||||
case EventType::UpdateEphemeralSystemClock: {
|
||||
m_ephemeral_clock_event->Clear(m_system.Kernel());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/glue/time/alarm_worker.h"
|
||||
@@ -37,17 +33,17 @@ public:
|
||||
|
||||
void StartThread();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T GetSettingsItemValue(const std::string& category, const std::string& name);
|
||||
|
||||
void ThreadFunc(std::stop_token stop_token);
|
||||
|
||||
Core::System& m_system;
|
||||
KernelHelpers::ServiceContext m_ctx;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
|
||||
std::mutex m_process_mutex;
|
||||
std::condition_variable m_process_cv;
|
||||
std::stop_source m_stop_source;
|
||||
|
||||
std::jthread m_thread;
|
||||
Kernel::KEvent* m_event{};
|
||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
||||
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
|
||||
|
||||
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
||||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
|
||||
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
|
||||
{700, nullptr, "ActivateUniquePad"},
|
||||
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
||||
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
||||
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
struct RegisterData {
|
||||
std::array<u8, 0x68> data;
|
||||
|
||||
@@ -71,14 +71,17 @@ public:
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
u64 MemoryRead(u64 vaddr, size_t size) override {
|
||||
switch (size) {
|
||||
case sizeof(u64): return ReadMemory<u64>(vaddr);
|
||||
case sizeof(u32): return ReadMemory<u32>(vaddr);
|
||||
case sizeof(u16): return ReadMemory<u16>(vaddr);
|
||||
case sizeof(u8): return ReadMemory<u8>(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return ReadMemory<u8>(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
return ReadMemory<u16>(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
return ReadMemory<u32>(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return ReadMemory<u64>(vaddr);
|
||||
}
|
||||
u128 MemoryRead128(u64 vaddr) override {
|
||||
return ReadMemory<u128>(vaddr);
|
||||
@@ -86,19 +89,22 @@ public:
|
||||
std::string MemoryReadCString(u64 vaddr) {
|
||||
std::string result{};
|
||||
u8 next;
|
||||
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
|
||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
||||
result += char(next);
|
||||
return result;
|
||||
}
|
||||
|
||||
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
|
||||
switch (size) {
|
||||
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
|
||||
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
|
||||
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
|
||||
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
WriteMemory<u8>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
WriteMemory<u16>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
WriteMemory<u32>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
WriteMemory<u64>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, u128 value) override {
|
||||
WriteMemory<u128>(vaddr, value);
|
||||
@@ -187,14 +193,14 @@ public:
|
||||
// The loaded NRO file has ELF relocations that must be processed before it can run.
|
||||
// Normally this would be processed by RTLD, but in HLE context, we don't have
|
||||
// the linker available, so we have to do it ourselves.
|
||||
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
|
||||
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||
const VAddr mod_offset{callbacks->MemoryRead32(4)};
|
||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||
return false;
|
||||
|
||||
// For more info about dynamic entries, see the ELF ABI specification:
|
||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
|
||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
|
||||
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
|
||||
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
|
||||
VAddr rela_dyn = 0, relr_dyn = 0;
|
||||
size_t num_rela = 0, num_relr = 0;
|
||||
while (true) {
|
||||
@@ -216,8 +222,8 @@ public:
|
||||
for (size_t i = 0; i < num_rela; i++) {
|
||||
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
||||
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
||||
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
|
||||
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
|
||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +231,7 @@ public:
|
||||
for (size_t i = 0; i < num_relr; i++) {
|
||||
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
||||
const auto incr = [&](VAddr where) {
|
||||
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
|
||||
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
|
||||
};
|
||||
if ((relr & 1) == 0) {
|
||||
// where pointer
|
||||
@@ -288,7 +294,7 @@ public:
|
||||
if (argument_stack.size() > 8) {
|
||||
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
||||
for (size_t i = 8; i < argument_stack.size(); i++)
|
||||
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
|
||||
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
|
||||
jit->SetSP(new_sp);
|
||||
}
|
||||
// Reset the call state for the next invocation
|
||||
@@ -379,17 +385,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
|
||||
if (dest < src) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
|
||||
MemoryWrite8(dest + i, MemoryRead8(src + i));
|
||||
} else {
|
||||
for (size_t i = n; i > 0; i--)
|
||||
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
|
||||
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
|
||||
}
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
||||
const VAddr dest{parent.jit->GetRegister(0)};
|
||||
const u64 c{parent.jit->GetRegister(1)};
|
||||
const size_t n{parent.jit->GetRegister(2)};
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite(dest + i, u8(c), sizeof(u8));
|
||||
MemoryWrite8(dest + i, u8(c));
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
||||
// X0 contains a char* for a symbol to resolve
|
||||
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
||||
@@ -416,7 +422,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
|
||||
auto const inst = MemoryRead(pc, sizeof(u32));
|
||||
auto const inst = MemoryRead32(pc);
|
||||
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
||||
parent.jit->HaltExecution();
|
||||
}
|
||||
|
||||
@@ -98,10 +98,8 @@ ServerManager::~ServerManager() {
|
||||
// Signal stop.
|
||||
m_stop_source.request_stop();
|
||||
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. Schedule
|
||||
// Wait for processing to stop.
|
||||
m_stopped.Wait();
|
||||
m_threads.clear();
|
||||
|
||||
@@ -255,14 +253,6 @@ 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() {
|
||||
SCOPE_EXIT {
|
||||
m_stopped.Set();
|
||||
@@ -295,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
|
||||
this->LinkDeferred();
|
||||
|
||||
// If we're done, return before we start waiting.
|
||||
if (m_stop_source.stop_requested())
|
||||
if (m_stop_source.stop_requested()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
|
||||
if (selected == std::addressof(*m_wakeup_holder)) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -52,7 +49,6 @@ public:
|
||||
|
||||
Result LoopProcess();
|
||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||
void NotifyShutdown();
|
||||
|
||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
{"friends", &Friend::LoopProcess},
|
||||
{"settings", &Set::LoopProcess},
|
||||
{"psc", &PSC::LoopProcess},
|
||||
{"glue", &Glue::LoopProcess},
|
||||
{"grc", &GRC::LoopProcess},
|
||||
{"hid", &HID::LoopProcess},
|
||||
{"jit", &JIT::LoopProcess},
|
||||
@@ -152,7 +153,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
{"usb", &USB::LoopProcess},
|
||||
{"i2c", &I2C::LoopProcess},
|
||||
{"gpio", &GPIO::LoopProcess},
|
||||
{"glue", &Glue::LoopProcess},
|
||||
})
|
||||
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
||||
}
|
||||
|
||||
@@ -305,14 +305,16 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
||||
SM::~SM() = default;
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
Kernel::KEvent* deferral_event{};
|
||||
server_manager->ManageDeferral(&deferral_event);
|
||||
system.ServiceManager().SetDeferralEvent(deferral_event);
|
||||
service_manager.SetDeferralEvent(deferral_event);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/vi/conductor.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
#include "core/hle/service/vi/display_list.h"
|
||||
@@ -30,29 +29,24 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
m_signal.Set();
|
||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||
});
|
||||
m_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](s64 time,
|
||||
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);
|
||||
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() {
|
||||
auto const stop_token = m_thread.get_stop_token();
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
|
||||
Common::SetCurrentThreadToPerformanceCores();
|
||||
while (!stop_token.stop_requested()) {
|
||||
m_signal.Wait();
|
||||
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
|
||||
break;
|
||||
}
|
||||
this->ProcessVsync();
|
||||
}
|
||||
});
|
||||
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
|
||||
} 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());
|
||||
});
|
||||
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);
|
||||
}
|
||||
@@ -60,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
|
||||
|
||||
Conductor::~Conductor() {
|
||||
m_system.CoreTiming().UnscheduleEvent(m_event);
|
||||
if (m_thread.joinable()) {
|
||||
|
||||
if (m_system.IsMulticore()) {
|
||||
m_thread.request_stop();
|
||||
m_signal.Set();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +83,22 @@ 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 {
|
||||
const auto& settings = Settings::values;
|
||||
auto speed_scale = 1.f;
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// 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/scope_exit.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
@@ -1275,8 +1266,3 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -254,16 +254,14 @@ struct UnrecognizedInstruction {
|
||||
|
||||
struct CheatVmOpcode {
|
||||
bool begin_conditional_block{};
|
||||
std::variant<
|
||||
std::monostate,
|
||||
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
|
||||
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
|
||||
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
||||
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
||||
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
||||
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
|
||||
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
|
||||
> opcode{};
|
||||
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
|
||||
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
|
||||
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
||||
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
||||
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
||||
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
|
||||
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
|
||||
opcode{};
|
||||
};
|
||||
|
||||
class DmntCheatVm {
|
||||
|
||||
@@ -33,10 +33,18 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
|
||||
|
||||
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
||||
switch (width) {
|
||||
case sizeof(u64): return memory.Write64(addr, value);
|
||||
case sizeof(u32): return memory.Write32(addr, u32(value));
|
||||
case sizeof(u16): return memory.Write16(addr, u16(value));
|
||||
case sizeof(u8): return memory.Write8(addr, u8(value));
|
||||
case 1:
|
||||
memory.Write8(addr, static_cast<u8>(value));
|
||||
break;
|
||||
case 2:
|
||||
memory.Write16(addr, static_cast<u16>(value));
|
||||
break;
|
||||
case 4:
|
||||
memory.Write32(addr, static_cast<u32>(value));
|
||||
break;
|
||||
case 8:
|
||||
memory.Write64(addr, value);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -23,66 +23,30 @@
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, value, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
// X2 = value
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
code.LDR(X0, l_this);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -95,7 +59,6 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
||||
ABI_PushRegisters(code, save_regs, 0);
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
code.MOV(Xscratch0, X0);
|
||||
@@ -119,7 +82,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
|
||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -138,7 +101,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -152,7 +115,6 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, Xscratch1);
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
ABI_PopRegisters(code, save_regs, 0);
|
||||
@@ -174,9 +136,12 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
||||
[&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
})
|
||||
? 0
|
||||
: 1;
|
||||
};
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
@@ -214,27 +179,26 @@ void A32AddressSpace::EmitPrelude() {
|
||||
|
||||
UnprotectCodeMemory();
|
||||
|
||||
|
||||
prelude_info.read_memory_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
|
||||
|
||||
@@ -22,66 +22,30 @@
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, value, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
// X2 = value
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
code.LDR(X0, l_this);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -94,7 +58,6 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
||||
ABI_PushRegisters(code, save_regs, 0);
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
code.MOV(Xscratch0, X0);
|
||||
@@ -118,7 +81,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
|
||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -137,7 +100,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -151,7 +114,6 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, Xscratch1);
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
ABI_PopRegisters(code, save_regs, 0);
|
||||
@@ -171,10 +133,14 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
using namespace oaknut::util;
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
||||
[&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
})
|
||||
? 0
|
||||
: 1;
|
||||
};
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
@@ -380,30 +346,30 @@ void A64AddressSpace::EmitPrelude() {
|
||||
|
||||
UnprotectCodeMemory();
|
||||
|
||||
prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf);
|
||||
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||
|
||||
@@ -145,7 +145,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ReturnFromRunCode:
|
||||
c.B(prelude_info.return_from_run_code);
|
||||
break;
|
||||
// { this, vaddr, size }
|
||||
case LinkTarget::ReadMemory8:
|
||||
c.BL(prelude_info.read_memory_8);
|
||||
break;
|
||||
@@ -161,7 +160,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ReadMemory128:
|
||||
c.BL(prelude_info.read_memory_128);
|
||||
break;
|
||||
// { this, vaddr, size }
|
||||
case LinkTarget::WrappedReadMemory8:
|
||||
c.BL(prelude_info.wrapped_read_memory_8);
|
||||
break;
|
||||
@@ -177,7 +175,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WrappedReadMemory128:
|
||||
c.BL(prelude_info.wrapped_read_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::ExclusiveReadMemory8:
|
||||
c.BL(prelude_info.exclusive_read_memory_8);
|
||||
break;
|
||||
@@ -193,7 +190,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ExclusiveReadMemory128:
|
||||
c.BL(prelude_info.exclusive_read_memory_128);
|
||||
break;
|
||||
// { this, vaddr, value, size }
|
||||
case LinkTarget::WriteMemory8:
|
||||
c.BL(prelude_info.write_memory_8);
|
||||
break;
|
||||
@@ -209,7 +205,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WriteMemory128:
|
||||
c.BL(prelude_info.write_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::WrappedWriteMemory8:
|
||||
c.BL(prelude_info.wrapped_write_memory_8);
|
||||
break;
|
||||
@@ -225,7 +220,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WrappedWriteMemory128:
|
||||
c.BL(prelude_info.wrapped_write_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::ExclusiveWriteMemory8:
|
||||
c.BL(prelude_info.exclusive_write_memory_8);
|
||||
break;
|
||||
|
||||
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
|
||||
void A32EmitX64::GenFastmemFallbacks() {
|
||||
const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||
// params = { this, vaddr, size }
|
||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||
}
|
||||
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
callback.EmitCall(code);
|
||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||
@@ -76,7 +76,6 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||
// params = { this, vaddr, value, size }
|
||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||
@@ -93,9 +92,10 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
}
|
||||
}
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||
callback.EmitCall(code);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||
code.ret();
|
||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
|
||||
@@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
#undef Axx
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||
@@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() {
|
||||
void A64EmitX64::GenFastmemFallbacks() {
|
||||
const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||
// params = { this, vaddr, size }
|
||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||
}
|
||||
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
callback.EmitCall(code);
|
||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||
@@ -224,7 +224,6 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||
// params = { this, vaddr, value, size }
|
||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||
@@ -241,9 +240,10 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
}
|
||||
}
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||
callback.EmitCall(code);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||
code.ret();
|
||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
|
||||
@@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
#undef Axx
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||
@@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -59,13 +56,16 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
// Neither fastmem nor page table: Use callbacks
|
||||
if constexpr (bitsize == 128) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.CallFunction(memory_read_128);
|
||||
ctx.reg_alloc.DefineValue(code, inst, xmm1);
|
||||
} else {
|
||||
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
|
||||
code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||
}
|
||||
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr);
|
||||
code.CallFunction(memory_write_128);
|
||||
} else {
|
||||
// { this, vaddr, value, size }
|
||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
|
||||
code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT);
|
||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||
}
|
||||
if (ordered) code.mfence();
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
EmitCheckMemoryAbort(ctx, inst);
|
||||
return;
|
||||
}
|
||||
@@ -230,11 +230,12 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
|
||||
code.CallLambda(
|
||||
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
});
|
||||
});
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||
} else {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
@@ -249,11 +250,12 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
||||
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
code.CallLambda(
|
||||
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
||||
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
});
|
||||
});
|
||||
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
|
||||
ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE);
|
||||
|
||||
|
||||
@@ -170,19 +170,13 @@ template<>
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
auto const marked_bit = *ctx.conf.page_table_marked_bit;
|
||||
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);
|
||||
}
|
||||
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||
code.jc(abort, code.T_NEAR);
|
||||
}
|
||||
// mask away attributes
|
||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||
code.test(page, page);
|
||||
} else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) {
|
||||
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||
} else {
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user