Compare commits

..

2 Commits

Author SHA1 Message Date
Eden CI 9cd24e85c2 [dist, android] Update translations from Transifex for Sep 21 (#4466)
Automatic translation update for Sep 21

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4466
2026-09-21 19:25:54 +02:00
xbzk 74b5e10dc5 [native] doesUpdateMatchProgram update mask fix (#4465)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Old silly bug i've been neglecting:
On Android, installing content to NAND, under some content id conditions (ie MK8D DLC), base/update IDs mismatch, and UI prompts "The content that you selected does not match this game. Install anyway?
Useless, yet fixed.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4465
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 22:56:06 +02:00
67 changed files with 15321 additions and 14270 deletions
+628 -583
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+548 -503
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+550 -508
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+573 -527
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( 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& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL; const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
if (update_id == program_id) { if (update_id == program_id) {
return true; return true;
} }
@@ -289,6 +289,7 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string> <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="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="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_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string> <string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string> <string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,6 +318,13 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string> <string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string> <string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</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">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string> <string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string> <string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -328,12 +335,14 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string> <string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string> <string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</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_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить 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_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string> <string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string> <string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</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_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string> <string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string> <string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <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_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string> <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string> <string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -290,6 +291,25 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string> <string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string> <string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</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">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string> <string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string> <string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -304,6 +324,13 @@
<string name="frame_gen_target_rate_60">60 FPS</string> <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_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 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">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string> <string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string> <string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -314,12 +341,14 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string> <string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string> <string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</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_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 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_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string> <string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string> <string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string> <string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string> <string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string> <string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string> <string name="lossless_scaling_installed">已安装</string>
@@ -611,6 +640,11 @@
<string name="log">日志记录</string> <string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string> <string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</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 --> <!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string> <string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string> <string name="gpu_log_level">日志等级</string>
@@ -894,6 +928,8 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string> <string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string> <string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string> <string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</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="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string> <string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</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="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string> <string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string> <string name="update_download_failed">更新下載失敗</string>
@@ -1020,9 +1020,40 @@
<string name="memory_6gb">6GB (不穩定)</string> <string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</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 --> <!-- Temperature Units -->
<string name="temperature_celsius">攝氏</string> <string name="temperature_celsius">攝氏</string>
<string name="temperature_fahrenheit">華氏</string> <string name="temperature_fahrenheit">華氏</string>
<!-- Memory Sizes --> <!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string> <string name="memory_byte_shorthand">B</string>
@@ -1035,6 +1066,8 @@
<string name="renderer_none"></string> <string name="renderer_none"></string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string> <string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy --> <!-- DMA Accuracy -->
+1 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -19,10 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
} }
} }
void ADSP::NotifyShutdown() {
opus_decoder->NotifyShutdown();
}
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
return *audio_renderer; return *audio_renderer;
} }
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -45,7 +45,6 @@ public:
explicit ADSP(Core::System& system, Sink::Sink& sink); explicit ADSP(Core::System& system, Sink::Sink& sink);
~ADSP() = default; ~ADSP() = default;
void NotifyShutdown();
AudioRenderer::AudioRenderer& AudioRenderer(); AudioRenderer::AudioRenderer& AudioRenderer();
OpusDecoder::OpusDecoder& OpusDecoder(); OpusDecoder::OpusDecoder& OpusDecoder();
@@ -82,9 +82,6 @@ void OpusDecoder::Main(std::stop_token stop_token) {
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token); auto msg = Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Shutdown: case Shutdown:
Send(Direction::Host, Message::ShutdownOK); Send(Direction::Host, Message::ShutdownOK);
@@ -269,10 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
} }
} }
void OpusDecoder::NotifyShutdown() {
init_thread.request_stop();
main_thread.request_stop();
Send(Direction::DSP, Message::Shutdown);
}
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder
@@ -67,8 +67,6 @@ public:
shared_memory = &shared_memory_; shared_memory = &shared_memory_;
} }
void NotifyShutdown();
private: private:
/** /**
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
-5
View File
@@ -31,11 +31,6 @@ void AudioCore::CreateSinks() {
input_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_input_device_id.GetValue()); input_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_input_device_id.GetValue());
} }
void AudioCore::NotifyShutdown() {
audio_manager->NotifyShutdown();
adsp->NotifyShutdown();
}
void AudioCore::Shutdown() { void AudioCore::Shutdown() {
audio_manager->Shutdown(); audio_manager->Shutdown();
} }
-2
View File
@@ -24,8 +24,6 @@ public:
explicit AudioCore(Core::System& system); explicit AudioCore(Core::System& system);
~AudioCore(); ~AudioCore();
void NotifyShutdown();
/** /**
* Shutdown the audio core. * Shutdown the audio core.
*/ */
+1 -6
View File
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
const auto timed_out = events.Wait(l, std::chrono::seconds{2}); const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) { if (events.CheckAudioEventSet(Event::Type::Max)) {
break; break;
} }
@@ -34,11 +34,6 @@ AudioManager::AudioManager() {
}); });
} }
void AudioManager::NotifyShutdown() {
events.SetAudioEvent(Event::Type::Max, true);
thread.request_stop();
}
void AudioManager::Shutdown() { void AudioManager::Shutdown() {
events.SetAudioEvent(Event::Type::Max, true); events.SetAudioEvent(Event::Type::Max, true);
if (thread.joinable()) { if (thread.joinable()) {
+3 -4
View File
@@ -39,10 +39,9 @@ class AudioManager {
public: public:
explicit AudioManager(); explicit AudioManager();
/// @brief Notify of impending shutdown /**
void NotifyShutdown(); * Shutdown the audio manager.
*/
/// @brief Shutdown the audio manager.
void Shutdown(); 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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())}; auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) { if (in_use == buffer_params->in_use && !buffer_unmapped) {
error_info.error_code = ResultSuccess; error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0); error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2; out_status.writeOffset = current_state->last_pos2;
+20 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
}; };
struct DeviceInParameter { struct DeviceInParameter {
/* 0x000 */ u8 name[0x100]; /* 0x000 */ char name[0x100];
/* 0x100 */ u32 input_count; /* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs; /* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ u8 unk10A[0x1]; /* 0x10A */ char unk10A[0x1];
/* 0x10B */ bool downmix_enabled; /* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff; /* 0x10C */ std::array<f32, 4> downmix_coeff;
}; };
@@ -43,7 +43,7 @@ public:
struct DeviceState { struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info; /* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff; /* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ u8 unk18[0x18]; /* 0x18 */ char unk18[0x18];
}; };
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!"); static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos; /* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format; /* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs; /* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ u8 in_use; /* 0x22 */ bool in_use;
/* 0x23 */ u8 unk23[0x5]; /* 0x23 */ char unk23[0x5];
}; };
static_assert(sizeof(CircularBufferInParameter) == 0x28, static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!"); "CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2; /* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos; /* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos; /* 0x08 */ u32 last_pos;
/* 0x0C */ u8 unk0C[0x4]; /* 0x0C */ char unk0C[0x4];
/* 0x10 */ AddressInfo address_info; /* 0x10 */ AddressInfo address_info;
}; };
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!"); static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter { struct InParameter {
/* 0x000 */ Type type; /* 0x000 */ Type type;
/* 0x001 */ u8 in_use; /* 0x001 */ bool in_use;
/* 0x004 */ u32 node_id; /* 0x004 */ u32 node_id;
/* 0x008 */ u8 unk08[0x18]; /* 0x008 */ char unk08[0x18];
union { union {
/* 0x020 */ DeviceInParameter device; /* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer; /* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus { struct OutStatus {
/* 0x00 */ u32 writeOffset; /* 0x00 */ u32 writeOffset;
/* 0x04 */ u8 unk04[0x1C]; /* 0x04 */ char unk04[0x1C];
}; // size == 0x20 }; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!"); static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,18 +162,19 @@ public:
u8* GetParameter(); u8* GetParameter();
protected: protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink /// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{}; std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink /// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{}; std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
/// Type of this sink parameter{};
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+21 -46
View File
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system} : kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
, fs_controller{system} reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
, hid_core{kernel}
, cpu_manager{system}
, reporter{system}
, profile_manager{}
{}
u64 program_id; u64 program_id;
@@ -129,12 +124,6 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
applet_manager.emplace(system);
frontend_applets.emplace(system);
apm_controller.emplace(core_timing);
arp_manager.emplace();
profile_manager.emplace();
// Create a default fs if one doesn't already exist. // Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) { if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -144,7 +133,7 @@ struct System::Impl {
} }
// Create default implementations of applets if one is not provided. // Create default implementations of applets if one is not provided.
frontend_applets->SetDefaultAppletsIfMissing(); frontend_applets.SetDefaultAppletsIfMissing();
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -387,7 +376,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
@@ -439,27 +428,13 @@ struct System::Impl {
core_timing.SyncPause(false); core_timing.SyncPause(false);
Network::CancelPendingSocketOperations(); Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true); kernel.SuspendEmulation(true);
kernel.ShutdownCores();
// Notify services helpers of shutdown
audio_core->NotifyShutdown();
// Wait for threads/services to join
kernel.CloseServices(); kernel.CloseServices();
kernel.ShutdownCores();
// service shutdown
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
frontend_applets.reset();
applet_manager.reset();
apm_controller.reset();
arp_manager.reset();
profile_manager.reset();
fs_controller.Reset(); fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
core_timing.ClearPendingEvents(); core_timing.ClearPendingEvents();
core_timing.Reset();
app_loader.reset(); app_loader.reset();
audio_core.reset(); audio_core.reset();
gpu_core.reset(); gpu_core.reset();
@@ -477,7 +452,7 @@ struct System::Impl {
} }
// Reset all glue registrations // Reset all glue registrations
arp_manager->ResetAll(); arp_manager.ResetAll();
LOG_DEBUG(Core, "Shutdown OK"); LOG_DEBUG(Core, "Shutdown OK");
} }
@@ -507,13 +482,13 @@ struct System::Impl {
CpuManager cpu_manager; CpuManager cpu_manager;
Reporter reporter; Reporter reporter;
/// Applets /// Applets
std::optional<Service::AM::AppletManager> applet_manager; Service::AM::AppletManager applet_manager;
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets; Service::AM::Frontend::FrontendAppletHolder frontend_applets;
/// APM (Performance) services /// APM (Performance) services
std::optional<Service::APM::Controller> apm_controller; Service::APM::Controller apm_controller{core_timing};
/// Service State /// Service State
std::optional<Service::Glue::ARPManager> arp_manager; Service::Glue::ARPManager arp_manager;
std::optional<Service::Account::ProfileManager> profile_manager; Service::Account::ProfileManager profile_manager;
/// Network instance /// Network instance
Network::NetworkInstance network_instance; Network::NetworkInstance network_instance;
Core::SpeedLimiter speed_limiter; Core::SpeedLimiter speed_limiter;
@@ -840,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
} }
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets->SetFrontendAppletSet(std::move(set)); impl->frontend_applets.SetFrontendAppletSet(std::move(set));
} }
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return *impl->frontend_applets; return impl->frontend_applets;
} }
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return *impl->frontend_applets; return impl->frontend_applets;
} }
Service::AM::AppletManager& System::GetAppletManager() { Service::AM::AppletManager& System::GetAppletManager() {
return *impl->applet_manager; return impl->applet_manager;
} }
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -893,27 +868,27 @@ const Reporter& System::GetReporter() const {
} }
Service::Glue::ARPManager& System::GetARPManager() { Service::Glue::ARPManager& System::GetARPManager() {
return *impl->arp_manager; return impl->arp_manager;
} }
const Service::Glue::ARPManager& System::GetARPManager() const { const Service::Glue::ARPManager& System::GetARPManager() const {
return *impl->arp_manager; return impl->arp_manager;
} }
Service::APM::Controller& System::GetAPMController() { Service::APM::Controller& System::GetAPMController() {
return *impl->apm_controller; return impl->apm_controller;
} }
const Service::APM::Controller& System::GetAPMController() const { const Service::APM::Controller& System::GetAPMController() const {
return *impl->apm_controller; return impl->apm_controller;
} }
Service::Account::ProfileManager& System::GetProfileManager() { Service::Account::ProfileManager& System::GetProfileManager() {
return *impl->profile_manager; return impl->profile_manager;
} }
const Service::Account::ProfileManager& System::GetProfileManager() const { const Service::Account::ProfileManager& System::GetProfileManager() const {
return *impl->profile_manager; return impl->profile_manager;
} }
void System::SetExitLocked(bool locked) { void System::SetExitLocked(bool locked) {
+2 -2
View File
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
void CoreTiming::Reset() { void CoreTiming::Reset() {
paused = true; paused = true;
pause_event.Set();
event.Set();
if (timer_thread.joinable()) { if (timer_thread.joinable()) {
timer_thread.request_stop(); timer_thread.request_stop();
pause_event.Set();
event.Set();
timer_thread.join(); timer_thread.join();
} }
has_started = false; has_started = false;
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept { void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked()); ASSERT(this->IsLocked());
for (auto* thread : m_woken_dummy_threads) if (m_woken_dummy_threads.size() > 0) {
thread->DummyThreadEndWait(kernel); for (auto* thread : m_woken_dummy_threads)
m_woken_dummy_threads.clear(); thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
}
} }
} // namespace Kernel } // namespace Kernel
+1
View File
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
KThread* cur_child = nullptr; KThread* cur_child = nullptr;
{ {
KScopedLightLock proc_lk(process->GetListLock()); KScopedLightLock proc_lk(process->GetListLock());
auto& thread_list = process->GetThreadList(); auto& thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
+10 -4
View File
@@ -1236,13 +1236,18 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request. // Determine if this is the first termination request.
// Perform an atomic compare-and-swap from false to true. const bool first_request = [&]() -> bool {
bool expected = false; // 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 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 the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) { if (this->GetState() == ThreadState::Initialized) {
return m_thread_state = ThreadState::Terminated; m_thread_state = ThreadState::Terminated;
return ThreadState::Terminated;
} }
// Register the terminating dpc. // Register the terminating dpc.
@@ -1276,6 +1281,7 @@ namespace Kernel {
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true); m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
} }
} }
return this->GetState(); return this->GetState();
} }
+5 -35
View File
@@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
@@ -27,15 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
} }
} }
KWorkerTaskManager::KWorkerTaskManager() {} KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
KWorkerTaskManager::~KWorkerTaskManager() {
if (m_waiting_thread.joinable()) {
m_waiting_thread.request_stop();
m_task_cv.notify_one();
m_waiting_thread.join();
}
}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count); ASSERT(type <= WorkerType::Count);
@@ -44,31 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
m_waiting_thread.QueueWork([&kernel, task]() {
// spawn thread on demand // Do the task.
if (!m_waiting_thread.joinable()) { task->DoWorkerTask(kernel);
LOG_INFO(Kernel, "spawning KWorkerTaskManager thread"); });
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
KWorkerTask* t;
{
std::unique_lock lk{m_task_mutex};
m_task_cv.wait(lk);
if (stop_token.stop_requested())
break;
t = m_task_queue.back();
m_task_queue.pop_back();
}
t->DoWorkerTask(kernel);
}
});
}
{
std::scoped_lock lk{m_task_mutex};
m_task_queue.emplace_back(task);
}
m_task_cv.notify_one();
} }
} // namespace Kernel } // namespace Kernel
+3 -10
View File
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include <thread>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
@@ -24,16 +19,14 @@ public:
}; };
KWorkerTaskManager(); KWorkerTaskManager();
~KWorkerTaskManager();
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task); static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
private: private:
void AddTask(KernelCore& kernel, KWorkerTask* task); void AddTask(KernelCore& kernel, KWorkerTask* task);
std::jthread m_waiting_thread;
std::mutex m_task_mutex; private:
std::condition_variable m_task_cv; Common::ThreadWorker m_waiting_thread;
std::vector<KWorkerTask*> m_task_queue;
}; };
} // namespace Kernel } // namespace Kernel
+12 -16
View File
@@ -80,9 +80,7 @@ struct KernelCore::Impl {
// so it will be statically given a TLS slot anyways. // so it will be statically given a TLS slot anyways.
static inline thread_local ThreadLocalData tls_data = {}; static inline thread_local ThreadLocalData tls_data = {};
explicit Impl(Core::System& system_, KernelCore& kernel_) explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
: system{system_}
{
tls_data.lock = true; tls_data.lock = true;
} }
@@ -969,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer; return *impl->hardware_timer;
} }
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() { KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const { const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
void KernelCore::PrepareReschedule(std::size_t id) { void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1007,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get(); auto* manager = server_manager.get();
if (!impl->is_shutting_down) {
{
std::scoped_lock lk{impl->server_lock}; std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager)); impl->server_managers.emplace_back(std::move(server_manager));
} }
manager->LoopProcess(); manager->LoopProcess();
} }
@@ -1255,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
} }
void KernelCore::ShutdownCores() { void KernelCore::ShutdownCores() {
// Notify shutdown (pre-emptively)
for (auto& sm : impl->server_managers) {
sm->NotifyShutdown();
}
impl->TerminateAllProcesses(); impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this}; KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) for (auto* thread : impl->shutdown_threads)
+3 -2
View File
@@ -175,8 +175,9 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread. /// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id); void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer* ObjectListContainer(); KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
const KAutoObjectWithListContainer& ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking /// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown. /// leaks after emulation has been shutdown.
+3 -4
View File
@@ -151,8 +151,7 @@ public:
const bool is_initialized = this->IsInitialized(); const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0; uintptr_t arg = 0;
if (is_initialized) { if (is_initialized) {
if (auto const olc = kernel.ObjectListContainer(); olc) kernel.ObjectListContainer().Unregister(this);
olc->Unregister(this);
arg = this->GetPostDestroyArgument(); arg = this->GetPostDestroyArgument();
this->Finalize(kernel); this->Finalize(kernel);
} }
@@ -176,7 +175,7 @@ public:
public: public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) { static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size); kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer()->Initialize(); kernel.ObjectListContainer().Initialize();
} }
static Derived* Create(KernelCore& kernel) { static Derived* Create(KernelCore& kernel) {
@@ -187,7 +186,7 @@ public:
} }
static void Register(KernelCore& kernel, Derived* obj) { static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer()->Register(obj); return kernel.ObjectListContainer().Register(obj);
} }
static size_t GetObjectSize(KernelCore& kernel) { static size_t GetObjectSize(KernelCore& kernel) {
+8 -9
View File
@@ -26,25 +26,24 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
m_window_system.SetEventObserver(this); m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent)); m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() { m_thread = std::jthread([this](std::stop_token stop_token) {
auto const stop_token = m_stop_source.get_token(); Common::SetCurrentThreadName("am:EventObserver");
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token); auto* signaled_holder = this->WaitSignaled(stop_token);
if (stop_token.stop_requested() || !signaled_holder) if (!signaled_holder)
break; break;
this->Process(signaled_holder); this->Process(signaled_holder);
} }
m_process_cv.notify_one();
}); });
} }
EventObserver::~EventObserver() { EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish. // Signal thread and wait for processing to finish.
m_stop_source.request_stop(); if (m_thread.joinable()) {
m_wakeup_event.Signal(m_system.Kernel()); // Signal thread and wait for processing to finish.
{ m_thread.request_stop();
std::unique_lock lk{m_process_mutex}; m_wakeup_event.Signal(m_system.Kernel());
m_process_cv.wait(lk); m_thread.join();
} }
// Free remaining owned sessions. // Free remaining owned sessions.
+1 -5
View File
@@ -6,8 +6,6 @@
#pragma once #pragma once
#include <condition_variable>
#include <stop_token>
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
@@ -64,9 +62,7 @@ private:
MultiWait m_deferred_wait_list; MultiWait m_deferred_wait_list;
// Processing thread. // Processing thread.
std::stop_source m_stop_source{}; std::jthread m_thread{};
std::mutex m_process_mutex;
std::condition_variable m_process_cv{};
}; };
} // namespace Service::AM } // namespace Service::AM
+5 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
} }
TimeManager::TimeManager(Core::System& system) TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_time_zone_binary{system} : m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker} system,
{ m_steady_clock_resource,
m_file_timestamp_worker} {
m_time_m = m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
+168 -168
View File
@@ -20,17 +20,15 @@
namespace Service::Glue::Time { namespace Service::Glue::Time {
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker) TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
: m_system{system} FileTimestampWorker& file_timestamp_worker)
, m_ctx{m_system, "Glue:TimeWorker"} : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")} "Glue:TimeWorker:Event")},
, m_steady_clock_resource{steady_clock_resource} m_steady_clock_resource{steady_clock_resource},
, m_file_timestamp_worker{file_timestamp_worker} m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")} "Glue:TimeWorker:SteadyClockTimerEvent")},
, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")} m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
, m_alarm_worker{m_system, m_steady_clock_resource} m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
, m_pm_state_change_handler{m_alarm_worker}
{
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
"Time::SteadyClockEvent", "Time::SteadyClockEvent",
[this](s64 time, [this](s64 time,
@@ -49,16 +47,14 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
} }
TimeWorker::~TimeWorker() { TimeWorker::~TimeWorker() {
// Wait for processing to stop
m_stop_source.request_stop();
m_local_clock_event->Signal(m_system.Kernel()); m_local_clock_event->Signal(m_system.Kernel());
m_network_clock_event->Signal(m_system.Kernel()); m_network_clock_event->Signal(m_system.Kernel());
m_ephemeral_clock_event->Signal(m_system.Kernel()); m_ephemeral_clock_event->Signal(m_system.Kernel());
std::this_thread::sleep_for(std::chrono::milliseconds(16));
m_thread.request_stop();
m_event->Signal(m_system.Kernel()); m_event->Signal(m_system.Kernel());
{ m_thread.join();
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
}
m_ctx.CloseEvent(m_event); m_ctx.CloseEvent(m_event);
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
@@ -126,162 +122,166 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
} }
void TimeWorker::StartThread() { void TimeWorker::StartThread() {
m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() { m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, 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
);
}
if (stop_token.stop_requested()) void TimeWorker::ThreadFunc(std::stop_token stop_token) {
break; Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
switch (EventType(index)) { while (!stop_token.stop_requested()) {
case EventType::Exit: enum class EventType : s32 {
return; Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
case EventType::PowerStateChange: s32 index{};
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
}
break;
case EventType::SignalAlarms: if (m_pm_state_change_handler.m_priority != 0) {
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); // TODO: gIPmModuleService::GetEvent() 1
m_time_m->CheckAndSignalAlarms(); index = WaitAny(m_system.Kernel(),
break; &m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1
case EventType::UpdateLocalSystemClock: { );
m_local_clock_event->Clear(m_system.Kernel()); } else {
// TODO: gIPmModuleService::GetEvent() 1
Service::PSC::Time::SystemClockContext context{}; index = WaitAny(m_system.Kernel(),
R_ASSERT(m_local_clock->GetSystemClockContext(&context)); &m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
m_set_sys->SetUserSystemClockContext(context); &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_file_timestamp_worker.SetFilesystemPosixTime(); m_local_clock_event, // 3
break; m_network_clock_event, // 4
} m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
case EventType::UpdateNetworkSystemClock: { &m_timer_file_system->GetReadableEvent(), // 7
m_network_clock_event->Clear(m_system.Kernel()); m_standard_user_auto_correct_clock_event // 8
);
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();
}
} }
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 } // namespace Service::Glue::Time
+4 -8
View File
@@ -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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h" #include "core/hle/service/glue/time/alarm_worker.h"
@@ -37,17 +33,17 @@ public:
void StartThread(); void StartThread();
private:
template <typename T> template <typename T>
T GetSettingsItemValue(const std::string& category, const std::string& name); T GetSettingsItemValue(const std::string& category, const std::string& name);
void ThreadFunc(std::stop_token stop_token);
Core::System& m_system; Core::System& m_system;
KernelHelpers::ServiceContext m_ctx; KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::mutex m_process_mutex; std::jthread m_thread;
std::condition_variable m_process_cv;
std::stop_source m_stop_source;
Kernel::KEvent* m_event{}; Kernel::KEvent* m_event{};
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
+2 -8
View File
@@ -253,13 +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());
}
Result ServerManager::LoopProcess() { Result ServerManager::LoopProcess() {
SCOPE_EXIT { SCOPE_EXIT {
m_stopped.Set(); m_stopped.Set();
@@ -292,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
this->LinkDeferred(); this->LinkDeferred();
// If we're done, return before we start waiting. // If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) if (m_stop_source.stop_requested()) {
return nullptr; return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) { if (selected == std::addressof(*m_wakeup_holder)) {
-4
View File
@@ -1,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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -52,7 +49,6 @@ public:
Result LoopProcess(); Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads); void StartAdditionalHostThreads(const char* name, size_t num_threads);
void NotifyShutdown();
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
+1 -1
View File
@@ -117,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"friends", &Friend::LoopProcess}, {"friends", &Friend::LoopProcess},
{"settings", &Set::LoopProcess}, {"settings", &Set::LoopProcess},
{"psc", &PSC::LoopProcess}, {"psc", &PSC::LoopProcess},
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess}, {"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess}, {"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess}, {"jit", &JIT::LoopProcess},
@@ -152,7 +153,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"usb", &USB::LoopProcess}, {"usb", &USB::LoopProcess},
{"i2c", &I2C::LoopProcess}, {"i2c", &I2C::LoopProcess},
{"gpio", &GPIO::LoopProcess}, {"gpio", &GPIO::LoopProcess},
{"glue", &Glue::LoopProcess},
}) })
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
} }
+33 -23
View File
@@ -11,7 +11,6 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/vi/conductor.h" #include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h" #include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h" #include "core/hle/service/vi/display_list.h"
@@ -30,29 +29,24 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
}); });
if (system.IsMulticore()) { if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { m_event = Core::Timing::CreateEvent(
m_signal.Set(); "ScreenComposition",
return std::chrono::nanoseconds(this->GetNextTicks()); [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); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() { m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
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();
}
});
} else { } else {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { m_event = Core::Timing::CreateEvent(
this->ProcessVsync(); "ScreenComposition",
return std::chrono::nanoseconds(this->GetNextTicks()); [this](s64 time,
}); std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
} }
@@ -60,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
Conductor::~Conductor() { Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event); m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_thread.joinable()) {
if (m_system.IsMulticore()) {
m_thread.request_stop(); m_thread.request_stop();
m_signal.Set(); m_signal.Set();
m_thread.join();
} }
} }
@@ -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 { s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values; const auto& settings = Settings::values;
auto speed_scale = 1.f; auto speed_scale = 1.f;
-14
View File
@@ -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-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// gcc14 bug
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h" #include "core/memory/dmnt_cheat_types.h"
@@ -1275,8 +1266,3 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
} // namespace Core::Memory } // namespace Core::Memory
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
+9 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -254,16 +254,14 @@ struct UnrecognizedInstruction {
struct CheatVmOpcode { struct CheatVmOpcode {
bool begin_conditional_block{}; bool begin_conditional_block{};
std::variant< std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
std::monostate, LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, opcode{};
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
> opcode{};
}; };
class DmntCheatVm { class DmntCheatVm {
@@ -170,19 +170,13 @@ template<>
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
auto const marked_bit = *ctx.conf.page_table_marked_bit; code.bt(page, *ctx.conf.page_table_marked_bit);
if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) { code.jc(abort, code.T_NEAR);
code.test(page, s32(1 << marked_bit));
code.jnz(abort, code.T_NEAR);
} else {
code.bt(page, marked_bit);
code.jc(abort, code.T_NEAR);
}
} }
// mask away attributes // mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) { if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (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); code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.mov(tmp, ctx.conf.page_table_pointer_mask);
@@ -79,10 +79,8 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; UpdateButtonLifo(npad_entry, data->aruid);
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
@@ -90,19 +88,19 @@ void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; UpdateButtonLifo(npad_entry, data->aruid);
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) { auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; if (data == nullptr) {
UpdateButtonLifo(npad_entry, aruid); return;
} }
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid);
} }
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
@@ -496,30 +496,31 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
// Flatten all leading PHIs from each block into a vector // Flatten all leading PHIs from each block into a vector
std::vector<IR::Inst*> phi_instructions; std::vector<IR::Inst*> phi_instructions;
for (IR::Block* block : program.blocks) { for (IR::Block* block : program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) { for (auto it = block->begin(); it != block->end(); ++it) {
if (it->GetOpcode() != IR::Opcode::Phi) if (it->GetOpcode() != IR::Opcode::Phi)
break; break;
phi_instructions.push_back(&*it); phi_instructions.push_back(&*it);
} }
} }
if (phi_instructions.empty()) { if (phi_instructions.empty()) {
return; // nothing to patch return; // nothing to patch
} }
// Start "before" first PHI; advance on phi_arg == 0 // Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = size_t(-1); size_t phi_index = static_cast<size_t>(-1);
ctx.PatchDeferredPhi([&ctx, &phi_index, phi_insts = std::move(phi_instructions)](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) { ctx.PatchDeferredPhi([&](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
++phi_index; if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_instructions[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
} }
IR::Inst* phi = phi_insts[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
}
} // Anonymous namespace } // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) { std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) {
-122
View File
@@ -1,122 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <regex>
#include <string>
#include "common/logging.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs/vfs_real.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3_null.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3_vk.h"
class EmuWindow_Headless : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_Headless() = default;
~EmuWindow_Headless() = default;
bool IsShown() const override {
return true;
}
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override {
}
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return std::make_unique<DummyContext>();
}
};
int main(int argc, char *argv[]) {
struct {
Core::System system{};
EmuWindow_Headless emu_window{};
} state = {};
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
if (argc < 2) {
LOG_CRITICAL(Frontend, "Usage: {} [ms-to-run] [file]", argv[0]);
return EXIT_FAILURE;
}
std::chrono::milliseconds time_quanta = std::chrono::milliseconds{atoi(argv[1])};
auto const time_end = std::chrono::steady_clock::now() + time_quanta;
std::string filepath = argv[2];
// apply the log_filter setting
// the logger was initialized before and doesn't pick up the filter on its own
Common::Log::Filter filter;
filter.ParseFilterString("*:Info");
Common::Log::SetGlobalFilter(filter);
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return EXIT_FAILURE;
}
state.system.Initialize();
InputCommon::InputSubsystem input_subsystem{};
// Apply the command line arguments
state.system.ApplySettings();
Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Null);
state.system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
state.system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
state.system.GetFileSystemController().CreateFactories(*state.system.GetFilesystem());
state.system.GetUserChannel().clear();
Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application,
};
if (auto const load_result = state.system.Load(state.emu_window, filepath, load_parameters); load_result != Core::SystemResultStatus::Success) {
LOG_CRITICAL(Frontend, "load result = {}", u32(load_result));
// shutdown
void(state.system.Pause());
state.system.DetachDebugger();
state.system.ShutdownMainProcess();
return EXIT_FAILURE;
}
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
state.system.GPU().Start();
state.system.GetCpuManager().OnGpuReady();
// don't do anything, SDL3 already exists for us :D
state.system.RegisterExitCallback([] {});
void(state.system.Run());
if (state.system.DebuggerEnabled())
state.system.InitializeDebugger();
while (state.system.IsPoweredOn() && !state.system.GetExitRequested()) {
auto const time_now = std::chrono::steady_clock::now();
if (time_now > time_end)
break;
}
// shutdown
void(state.system.Pause());
state.system.DetachDebugger();
state.system.ShutdownMainProcess();
return EXIT_SUCCESS;
}
#define VMA_IMPLEMENTATION
#include "video_core/vulkan_common/vma.h"