mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-21 17:55:38 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cd24e85c2 | |||
| 74b5e10dc5 |
Vendored
+628
-583
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+548
-503
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+550
-508
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+573
-527
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
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,
|
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,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,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.
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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); });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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"
|
|
||||||
Reference in New Issue
Block a user