Compare commits

..

12 Commits

Author SHA1 Message Date
lizzie 1fe88221d1 2026-09-23 11:51:29
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 11:51:29 +00:00
lizzie e9bfd97c7f 2026-09-23 09:25:56
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 09:25:56 +00:00
lizzie 773c29ed85 2026-09-09 16:05:11
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 09f05f0313 2026-09-07 23:31:06
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 3b03ade8d8 2026-09-07 23:30:42
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 78cacac308 fix span shit 2026-09-23 10:42:17 +02:00
lizzie 84f43f2c63 [audio_core] remove dangling Core::System& references
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
PavelBARABANOV 3266c20e3f [ips_layer] read magic from IPS file and fix EOF check (#4469)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
PatchIPS was reading the magic bytes from the target file instead
of the IPS patch, and IsEOF used a strict > comparison that never
matched for 3-byte IPS EOF markers. This caused IPS patches to be
silently skipped, breaking the Russian localization mod for
Persona 5.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4469
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-23 00:36:47 +02:00
Eden CI 9cd24e85c2 [dist, android] Update translations from Transifex for Sep 21 (#4466)
Automatic translation update for Sep 21

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

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

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4465
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 22:56:06 +02:00
xbzk a277b62fe4 [fs] revert #4319 savedata_factory changes (#4462)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

This one reverts part of the PR 4319 which was aimed to allow child titles to inherit bundle settings.
The fix seems good so far, but by applying it to save folders caused a trap:
Now child's save folders move to bundle folder, hence saves will vanish, and the android/qt save tools are still pointing to old folders.
Since the save part was only a compliance fix, instead of fiddle in master i'd rather revert this part now, then only touch this again if any bug show up.
No worries. I'll be right here waiting.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4462
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 04:40:14 +02:00
lizzie 908b1e9a37 [dynarmic] fix bad arm codegen on relocations (#4458)
In an earlier version I had these as constants which were referred to via `db`, I haphazardly left the LDR and didn't notice.
Even still relocations apply to only 1 ins at a time.

Fixes regressions on #4158

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4458
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 00:29:28 +02:00
67 changed files with 15382 additions and 14008 deletions
+628 -583
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+548 -503
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+550 -508
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+573 -527
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) { for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL; const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
if (update_id == program_id) { if (update_id == program_id) {
return true; return true;
} }
@@ -289,6 +289,7 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string> <string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string> <string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string> <string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
<string name="post_processing_add">Añadir efecto</string>
<string name="post_processing_open_list">Abrir lista</string> <string name="post_processing_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string> <string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string> <string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,6 +318,13 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string> <string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string> <string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string> <string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
<string name="frame_gen_on">Вкл.</string>
<string name="frame_gen_off">Выкл.</string>
<string name="frame_gen_fixed">Фикс.</string>
<string name="frame_gen_flow_auto">Авто</string>
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string> <string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string> <string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string> <string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -328,12 +335,14 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string> <string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string> <string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</string> <string name="frame_gen_unsupported">Генерация кадров недоступна</string>
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string> <string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить Lossless.dll</string> <string name="lossless_scaling_install">Установить Lossless.dll</string>
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string> <string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string> <string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string> <string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</string> <string name="frame_generation_supported">Поддерживается</string>
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string> <string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string> <string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string> <string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">错误与注意事项</string> <string name="notice_notification_channel_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string> <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string> <string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -290,6 +291,25 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string> <string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string> <string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string> <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
<string name="post_processing">后处理效果</string>
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
<string name="post_processing_add">添加效果</string>
<string name="post_processing_remove">移除</string>
<string name="post_processing_open_list">打开列表</string>
<string name="post_processing_close_list">关闭列表</string>
<string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
<string name="post_processing_presets">预设</string>
<string name="post_processing_preset_new">新建预设</string>
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
<string name="post_processing_preset_none">无预设</string>
<string name="post_processing_preset_delete">删除预设</string>
<string name="post_processing_preset_reset">重置预设值</string>
<string name="post_processing_reset">重置为默认值</string>
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
<string name="frame_gen">帧生成</string> <string name="frame_gen">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string> <string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string> <string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -304,6 +324,13 @@
<string name="frame_gen_target_rate_60">60 FPS</string> <string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string> <string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string> <string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on"></string>
<string name="frame_gen_off"></string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自动</string>
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
<string name="frame_gen_queue_target">帧队列目标</string> <string name="frame_gen_queue_target">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string> <string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string> <string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -314,12 +341,14 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string> <string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string> <string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</string> <string name="frame_gen_unsupported">帧生成不可用</string>
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string> <string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 Lossless.dll</string> <string name="lossless_scaling_install">安装 Lossless.dll</string>
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string> <string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string> <string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string> <string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string> <string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string> <string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string> <string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string> <string name="lossless_scaling_installed">已安装</string>
@@ -611,6 +640,11 @@
<string name="log">日志记录</string> <string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string> <string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string> <string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
<string name="extended_logging">开启扩展日志</string>
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
<string name="log_filter">日志筛选器</string>
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings --> <!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string> <string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string> <string name="gpu_log_level">日志等级</string>
@@ -894,6 +928,8 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string> <string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string> <string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string> <string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string> <string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
@@ -229,7 +229,7 @@
<string name="add_directory_success">遊戲目錄新增成功</string> <string name="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string> <string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string> <string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
<string name="update_available">偵測到新版本</string> <string name="update_available">發現新版本</string>
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string> <string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string> <string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string> <string name="update_download_failed">更新下載失敗</string>
@@ -1020,9 +1020,40 @@
<string name="memory_6gb">6GB (不穩定)</string> <string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string> <string name="memory_8gb">8GB (不穩定)</string>
<!-- CPU clock levels -->
<string name="clock_normal">正常</string>
<string name="clock_boost">加速</string>
<string name="clock_fast">超頻</string>
<!-- GPU clock levels -->
<string name="fast_gpu_normal">正常</string>
<string name="fast_gpu_medium">加速</string>
<string name="fast_gpu_high">超頻</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">極小 16MB</string>
<string name="gpu_texturesizeswizzle_small">小(32MB</string>
<string name="gpu_texturesizeswizzle_normal">正常(128MB</string>
<string name="gpu_texturesizeswizzle_large">大(256MB</string>
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">極小(4MB</string>
<string name="gpu_swizzle_low">小(8MB</string>
<string name="gpu_swizzle_normal">正常(16MB</string>
<string name="gpu_swizzle_medium">中(32MB</string>
<string name="gpu_swizzle_high">大(64MB</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">極小(32</string>
<string name="gpu_swizzlechunk_low">小(64</string>
<string name="gpu_swizzlechunk_normal">正常(128</string>
<string name="gpu_swizzlechunk_medium">中(256</string>
<string name="gpu_swizzlechunk_high">大(512</string>
<!-- Temperature Units --> <!-- Temperature Units -->
<string name="temperature_celsius">攝氏</string> <string name="temperature_celsius">攝氏</string>
<string name="temperature_fahrenheit">華氏</string> <string name="temperature_fahrenheit">華氏</string>
<!-- Memory Sizes --> <!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string> <string name="memory_byte_shorthand">B</string>
@@ -1035,6 +1066,8 @@
<string name="renderer_none"></string> <string name="renderer_none"></string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string> <string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy --> <!-- DMA Accuracy -->
+1 -1
View File
@@ -12,7 +12,7 @@
namespace AudioCore { namespace AudioCore {
AudioCore::AudioCore(Core::System& system) { AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace(); audio_manager.emplace(system);
CreateSinks(); CreateSinks();
// Must be created after the sinks // Must be created after the sinks
adsp.emplace(system, *output_sink); adsp.emplace(system, *output_sink);
+12 -15
View File
@@ -15,12 +15,12 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
Manager::Manager(Core::System& system_) : system{system_} { Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxInSessions; num_free_sessions = MaxInSessions;
} }
Result Manager::AcquireSessionId(size_t& session_id) { Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(const size_t session_id) { void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -41,21 +41,20 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager() { Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this)); system.AudioCore().GetAudioManager().SetInManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start() { void Manager::Start(Core::System& system) {
if (sessions_started) { if (sessions_started) {
return; return;
} }
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
for (auto& session : sessions) { for (auto& session : sessions) {
if (session) { if (session) {
@@ -66,21 +65,19 @@ void Manager::Start() {
sessions_started = true; sessions_started = true;
} }
void Manager::BufferReleaseAndRegister() { void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
std::scoped_lock l{mutex}; Manager* this_ = (Manager*)data;
for (auto& session : sessions) { std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) {
[[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LinkToManager(system);
LinkToManager();
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) { if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac"); names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
+10 -11
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,31 +33,29 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(size_t& session_id); Result AcquireSessionId(Core::System& system, size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(size_t session_id); void ReleaseSessionId(Core::System& system, const size_t session_id);
/** /**
* Link the audio in manager to the main audio manager. * Link the audio in manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(); Result LinkToManager(Core::System& system);
/** /**
* Start the audio in manager. * Start the audio in manager.
*/ */
void Start(); void Start(Core::System& system);
/** /// @brief Callback function, called by the audio manager when the audio in event is signalled.
* Callback function, called by the audio manager when the audio in event is signalled. static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept;
*/
void BufferReleaseAndRegister();
/** /**
* Get a list of audio in device names. * Get a list of audio in device names.
@@ -64,10 +65,8 @@ public:
* *
* @return Number of names written. * @return Number of names written.
*/ */
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter); u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxInSessions> session_ids{}; std::array<size_t, MaxInSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+7 -5
View File
@@ -11,8 +11,8 @@
namespace AudioCore { namespace AudioCore {
AudioManager::AudioManager() { AudioManager::AudioManager(Core::System& system) {
thread = std::jthread([this](std::stop_token stop_token) { thread = std::jthread([&](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager"); Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
@@ -25,7 +25,7 @@ AudioManager::AudioManager() {
const auto event_type = Event::Type(i); const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) { if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) { if (buffer_events[i]) {
buffer_events[i](); buffer_events[i](buffer_data[i], system);
} }
} }
events.SetAudioEvent(event_type, false); events.SetAudioEvent(event_type, false);
@@ -42,12 +42,13 @@ void AudioManager::Shutdown() {
} }
} }
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) { Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true); events.SetAudioEvent(Event::Type::AudioOutManager, true);
} }
@@ -56,12 +57,13 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
return Service::Audio::ResultOperationFailed; return Service::Audio::ResultOperationFailed;
} }
Result AudioManager::SetInManager(BufferEventFunc buffer_func) { Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true); events.SetAudioEvent(Event::Type::AudioInManager, true);
} }
+16 -18
View File
@@ -16,6 +16,10 @@
#include "audio_core/audio_event.h" #include "audio_core/audio_event.h"
namespace Core {
class System;
}
union Result; union Result;
namespace AudioCore { namespace AudioCore {
@@ -34,31 +38,24 @@ namespace AudioCore {
* This is only used by audio in and audio out. * This is only used by audio in and audio out.
*/ */
class AudioManager { class AudioManager {
using BufferEventFunc = std::function<void()>; using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
public: public:
explicit AudioManager(); explicit AudioManager(Core::System& system);
/** /**
* Shutdown the audio manager. * Shutdown the audio manager.
*/ */
void Shutdown(); void Shutdown();
/** /// Register the out manager, keeping a function to be called when the out event is signalled.
* Register the out manager, keeping a function to be called when the out event is signalled. /// @param buffer_func - Function to be called on signal.
* /// @return Result code.
* @param buffer_func - Function to be called on signal. Result SetOutManager(void *data, BufferEventFunc buffer_func);
* @return Result code.
*/
Result SetOutManager(BufferEventFunc buffer_func);
/** /// Register the in manager, keeping a function to be called when the in event is signalled.
* Register the in manager, keeping a function to be called when the in event is signalled. /// @param buffer_func - Function to be called on signal.
* /// @return Result code.
* @param buffer_func - Function to be called on signal. Result SetInManager(void *data, BufferEventFunc buffer_func);
* @return Result code.
*/
Result SetInManager(BufferEventFunc buffer_func);
/** /**
* Set an event to signalled, and signal the thread. * Set an event to signalled, and signal the thread.
@@ -73,8 +70,9 @@ private:
bool needs_update{}; bool needs_update{};
/// Events to be set and signalled /// Events to be set and signalled
Event events{}; Event events{};
/// Callbacks for each manager /// Callbacks (and user data) for each manager
std::array<BufferEventFunc, 3> buffer_events{}; std::array<BufferEventFunc, 3> buffer_events{};
std::array<void*, 3> buffer_data{};
/// General lock /// General lock
std::mutex lock{}; std::mutex lock{};
/// Main thread for waiting and callbacks /// Main thread for waiting and callbacks
+12 -20
View File
@@ -14,12 +14,12 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Manager::Manager(Core::System& system_) : system{system_} { Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxOutSessions; num_free_sessions = MaxOutSessions;
} }
Result Manager::AcquireSessionId(size_t& session_id) { Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(const size_t session_id) { void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -40,44 +40,36 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager() { Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this)); system.AudioCore().GetAudioManager().SetOutManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start() { void Manager::Start(Core::System& system) {
if (sessions_started) { if (!sessions_started) {
return;
}
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
for (auto& session : sessions) { for (auto& session : sessions) {
if (session) { if (session) {
session->StartSession(); session->StartSession();
} }
} }
sessions_started = true; sessions_started = true;
}
} }
void Manager::BufferReleaseAndRegister() { void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
std::scoped_lock l{mutex}; Manager* this_ = (Manager*)data;
for (auto& session : sessions) { std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetAudioOutDeviceNames(
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
names.emplace_back("DeviceOut");
return 1;
}
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+8 -15
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,42 +32,32 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(size_t& session_id); Result AcquireSessionId(Core::System& system, size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(size_t session_id); void ReleaseSessionId(Core::System& system, const size_t session_id);
/** /**
* Link this manager to the main audio manager. * Link this manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(); Result LinkToManager(Core::System& system);
/** /**
* Start the audio out manager. * Start the audio out manager.
*/ */
void Start(); void Start(Core::System& system);
/** /**
* Callback function, called by the audio manager when the audio out event is signalled. * Callback function, called by the audio manager when the audio out event is signalled.
*/ */
void BufferReleaseAndRegister(); static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept;
/**
* Get a list of audio out device names.
*
* @param names - Output container to write names to.
* @return Number of names written.
*/
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxOutSessions> session_ids{}; std::array<size_t, MaxOutSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+4 -4
View File
@@ -6,14 +6,14 @@
#include "audio_core/audio_render_manager.h" #include "audio_core/audio_render_manager.h"
#include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/system_manager.h"
#include "audio_core/common/feature_support.h" #include "audio_core/common/feature_support.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Manager::Manager(Core::System& system_) Manager::Manager(Core::System& system_)
: system{system_} : system_manager{std::make_unique<SystemManager>(system_)}
, system_manager{std::make_unique<SystemManager>(system)}
{ {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
} }
@@ -62,11 +62,11 @@ u32 Manager::GetSessionCount() const {
return session_count; return session_count;
} }
bool Manager::AddSystem(System& system_) { bool Manager::AddSystem(Renderer::System& system_) {
return system_manager->Add(system_); return system_manager->Add(system_);
} }
bool Manager::RemoveSystem(System& system_) { bool Manager::RemoveSystem(Renderer::System& system_) {
return system_manager->Remove(system_); return system_manager->Remove(system_);
} }
+5 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 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
@@ -71,7 +74,7 @@ public:
* @param system - The system to add. * @param system - The system to add.
* @return True if the system was successfully added, otherwise false. * @return True if the system was successfully added, otherwise false.
*/ */
bool AddSystem(System& system); bool AddSystem(Renderer::System& system);
/** /**
* Remove a renderer system from the manager. * Remove a renderer system from the manager.
@@ -79,7 +82,7 @@ public:
* @param system - The system to remove. * @param system - The system to remove.
* @return True if the system was successfully removed, otherwise false. * @return True if the system was successfully removed, otherwise false.
*/ */
bool RemoveSystem(System& system); bool RemoveSystem(Renderer::System& system);
/** /**
* Free a session id when the system wants to shut down. * Free a session id when the system wants to shut down.
@@ -89,8 +92,6 @@ public:
void ReleaseSessionId(s32 session_id); void ReleaseSessionId(s32 session_id);
private: private:
/// Core system
Core::System& system;
/// Session ids, -1 when in use /// Session ids, -1 when in use
std::array<s32, MaxRendererSessions> session_ids{}; std::array<s32, MaxRendererSessions> session_ids{};
/// Number of active renderers /// Number of active renderers
+24 -20
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, : manager{manager_}, parent_mutex{manager.mutex}, event{event_}
session_id_} {} , audio_system{system_, event, session_id_}
{}
void In::Free() { void In::Free(Core::System& system) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId()); manager.ReleaseSessionId(system, audio_system.GetSessionId());
} }
System& In::GetSystem() { System& In::GetSystem() {
return system; return audio_system;
} }
AudioIn::State In::GetState() { AudioIn::State In::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetState(); return audio_system.GetState();
} }
Result In::StartSystem() { Result In::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Start(); return audio_system.Start();
} }
void In::StartSession() { void In::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.StartSession(); audio_system.StartSession();
} }
Result In::StopSystem() { Result In::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Stop(); return audio_system.Stop();
} }
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) { Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) { if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
void In::ReleaseAndRegisterBuffers() { void In::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) { if (audio_system.GetState() == State::Started) {
system.ReleaseBuffers(); audio_system.ReleaseBuffers();
system.RegisterBuffers(); audio_system.RegisterBuffers();
} }
} }
bool In::FlushAudioInBuffers() { bool In::FlushAudioInBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.FlushAudioInBuffers(); return audio_system.FlushAudioInBuffers();
} }
u32 In::GetReleasedBuffers(std::span<u64> tags) { u32 In::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags); return audio_system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& In::GetBufferEvent() { Kernel::KReadableEvent& In::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
f32 In::GetVolume() const { f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetVolume(); return audio_system.GetVolume();
} }
void In::SetVolume(f32 volume) { void In::SetVolume(f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.SetVolume(volume); audio_system.SetVolume(volume);
} }
bool In::ContainsAudioBuffer(u64 tag) const { bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag); return audio_system.ContainsAudioBuffer(tag);
} }
u32 In::GetBufferCount() const { u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetBufferCount(); return audio_system.GetBufferCount();
} }
u64 In::GetPlayedSampleCount() const { u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount(); return audio_system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/** /**
* Free this audio in from the audio in manager. * Free this audio in from the audio in manager.
*/ */
void Free(); void Free(Core::System& system);
/** /**
* Get this audio in's system. * Get this audio in's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio in system /// Main audio in system
System system; System audio_system;
}; };
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+24 -20
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, : manager{manager_}, parent_mutex{manager.mutex}, event{event_}
session_id_} {} , audio_system{system_, event, session_id_}
{}
void Out::Free() { void Out::Free(Core::System& system) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId()); manager.ReleaseSessionId(system, audio_system.GetSessionId());
} }
System& Out::GetSystem() { System& Out::GetSystem() {
return system; return audio_system;
} }
AudioOut::State Out::GetState() { AudioOut::State Out::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetState(); return audio_system.GetState();
} }
Result Out::StartSystem() { Result Out::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Start(); return audio_system.Start();
} }
void Out::StartSession() { void Out::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.StartSession(); audio_system.StartSession();
} }
Result Out::StopSystem() { Result Out::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Stop(); return audio_system.Stop();
} }
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) { Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) { if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
void Out::ReleaseAndRegisterBuffers() { void Out::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) { if (audio_system.GetState() == State::Started) {
system.ReleaseBuffers(); audio_system.ReleaseBuffers();
system.RegisterBuffers(); audio_system.RegisterBuffers();
} }
} }
bool Out::FlushAudioOutBuffers() { bool Out::FlushAudioOutBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.FlushAudioOutBuffers(); return audio_system.FlushAudioOutBuffers();
} }
u32 Out::GetReleasedBuffers(std::span<u64> tags) { u32 Out::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags); return audio_system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& Out::GetBufferEvent() { Kernel::KReadableEvent& Out::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
f32 Out::GetVolume() const { f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetVolume(); return audio_system.GetVolume();
} }
void Out::SetVolume(const f32 volume) { void Out::SetVolume(const f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.SetVolume(volume); audio_system.SetVolume(volume);
} }
bool Out::ContainsAudioBuffer(const u64 tag) const { bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag); return audio_system.ContainsAudioBuffer(tag);
} }
u32 Out::GetBufferCount() const { u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetBufferCount(); return audio_system.GetBufferCount();
} }
u64 Out::GetPlayedSampleCount() const { u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount(); return audio_system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/** /**
* Free this audio out from the audio out manager. * Free this audio out from the audio out manager.
*/ */
void Free(); void Free(Core::System& system);
/** /**
* Get this audio out's system. * Get this audio out's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio out system /// Main audio out system
System system; System audio_system;
}; };
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+13 -18
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -34,42 +34,37 @@ constexpr std::array output_device_names{
AudioDevice::AudioDeviceName{"AudioExternalOutput"}, AudioDevice::AudioDeviceName{"AudioExternalOutput"},
}; };
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, const u32 revision)
const u32 revision) : applet_resource_user_id{applet_resource_user_id_}
: output_sink{system.AudioCore().GetOutputSink()}, , user_revision{revision}
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {} {}
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const { u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
std::span<const AudioDeviceName> names{}; std::span<const AudioDeviceName> names{};
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) { if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
names = usb_device_names; names = usb_device_names;
} else { } else {
names = device_names; names = device_names;
} }
const u32 out_count = u32((std::min)(out_buffer.size(), names.size()));
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), names.size()))}; for (u32 i = 0; i < out_count; i++)
for (u32 i = 0; i < out_count; i++) {
out_buffer[i] = names[i]; out_buffer[i] = names[i];
}
return out_count; return out_count;
} }
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const { u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), output_device_names.size()))}; const u32 out_count = u32((std::min)(out_buffer.size(), output_device_names.size()));
for (u32 i = 0; i < out_count; i++)
for (u32 i = 0; i < out_count; i++) {
out_buffer[i] = output_device_names[i]; out_buffer[i] = output_device_names[i];
}
return out_count; return out_count;
} }
void AudioDevice::SetDeviceVolumes(const f32 volume) { void AudioDevice::SetDeviceVolumes(Core::System& system, const f32 volume) {
output_sink.SetDeviceVolume(volume); system.AudioCore().GetOutputSink().SetDeviceVolume(volume);
} }
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const { f32 AudioDevice::GetDeviceVolume(Core::System& system, [[maybe_unused]] std::string_view name) const {
return output_sink.GetDeviceVolume(); return system.AudioCore().GetOutputSink().GetDeviceVolume();
} }
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+5 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 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
@@ -54,7 +57,7 @@ public:
* *
* @param volume - Volume to set. * @param volume - Volume to set.
*/ */
void SetDeviceVolumes(f32 volume); void SetDeviceVolumes(Core::System& system, f32 volume);
/** /**
* Get the volume for a given device name. * Get the volume for a given device name.
@@ -63,11 +66,9 @@ public:
* @param name - Name of the device to check. Unused. * @param name - Name of the device to check. Unused.
* @return Volume of the device. * @return Volume of the device.
*/ */
f32 GetDeviceVolume(std::string_view name) const; f32 GetDeviceVolume(Core::System& system, std::string_view name) const;
private: private:
/// Backend output sink for the device
Sink::Sink& output_sink;
/// Resource id this device is used for /// Resource id this device is used for
const u64 applet_resource_user_id; const u64 applet_resource_user_id;
/// User audio renderer revision /// User audio renderer revision
+18 -25
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,56 +16,46 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
: core{system_}, manager{manager_}, system{system_, rendered_event} {} : manager{manager_}
, audio_system{system_, rendered_event}
{}
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) {
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) { if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(system)) { if (!manager.AddSystem(audio_system)) {
LOG_ERROR(Service_Audio, LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more");
"Both Audio Render sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
} }
system_registered = true; system_registered = true;
} }
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id);
initialized = true;
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
applet_resource_user_id, session_id);
return ResultSuccess; return ResultSuccess;
} }
void Renderer::Finalize() { void Renderer::Finalize() {
auto session_id{system.GetSessionId()}; auto const session_id{audio_system.GetSessionId()};
audio_system.Finalize();
system.Finalize();
if (system_registered) { if (system_registered) {
manager.RemoveSystem(system); manager.RemoveSystem(audio_system);
system_registered = false; system_registered = false;
} }
manager.ReleaseSessionId(session_id); manager.ReleaseSessionId(session_id);
} }
System& Renderer::GetSystem() { System& Renderer::GetSystem() {
return system; return audio_system;
} }
void Renderer::Start() { void Renderer::Start() {
system.Start(); audio_system.Start();
} }
void Renderer::Stop() { void Renderer::Stop() {
system.Stop(); audio_system.Stop();
} }
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
std::span<u8> output) { return audio_system.Update(input, performance, output);
return system.Update(input, performance, output);
} }
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+4 -5
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -83,16 +86,12 @@ public:
std::span<u8> output); std::span<u8> output);
private: private:
/// System core
Core::System& core;
/// Manager this renderer is registered with /// Manager this renderer is registered with
Manager& manager; Manager& manager;
/// Is the audio renderer initialized?
bool initialized{};
/// Is the system registered with the manager? /// Is the system registered with the manager?
bool system_registered{}; bool system_registered{};
/// Audio render system, main driver of audio rendering /// Audio render system, main driver of audio rendering
System system; System audio_system;
}; };
} // namespace Renderer } // namespace Renderer
+10 -12
View File
@@ -100,8 +100,9 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
} }
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()}, : core{core_}
adsp_rendered_event{adsp_rendered_event_} {} , adsp_rendered_event{adsp_rendered_event_}
{}
Result System::Initialize(const AudioRendererParameterInternal& params, Result System::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
@@ -406,7 +407,7 @@ void System::Finalize() {
return; return;
} }
if (active) { if (IsActive()) {
Stop(); Stop();
} }
@@ -433,14 +434,12 @@ void System::Start() {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
frames_elapsed = 0; frames_elapsed = 0;
state = State::Started; state = State::Started;
active = true;
} }
void System::Stop() { void System::Stop() {
{ {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
state = State::Stopped; state = State::Stopped;
active = false;
} }
if (execution_mode == ExecutionMode::Auto) { if (execution_mode == ExecutionMode::Auto) {
@@ -480,8 +479,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result; return result;
} }
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer, result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer, memory_pool_count);
memory_pool_count);
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_Audio, "Failed to update Effects!"); LOG_ERROR(Service_Audio, "Failed to update Effects!");
return result; return result;
@@ -582,16 +580,16 @@ u32 System::GetRenderingDevice() const {
} }
bool System::IsActive() const { bool System::IsActive() const {
return active; return state == State::Started;
} }
void System::SendCommandToDsp() { void System::SendCommandToDsp() {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
if (initialized) { if (initialized) {
if (active) { if (IsActive()) {
terminate_event.Reset(); terminate_event.Reset();
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)}; const auto remaining_command_count = audio_renderer.GetRemainCommandCount(session_id);
u64 command_size{0}; u64 command_size{0};
if (remaining_command_count) { if (remaining_command_count) {
@@ -738,7 +736,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()}; const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
total_ticks_elapsed += end_time - start_time; total_ticks_elapsed += end_time - start_time;
num_command_lists_generated++; num_command_lists_generated++;
render_start_tick = audio_renderer.GetRenderingStartTick(session_id); render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
frames_elapsed++; frames_elapsed++;
return command_buffer.size; return command_buffer.size;
+3 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -219,12 +222,8 @@ public:
private: private:
/// Core system /// Core system
Core::System& core; Core::System& core;
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
/// Is this system initialized? /// Is this system initialized?
bool initialized{}; bool initialized{};
/// Is this system currently active?
std::atomic<bool> active{};
/// State of the system /// State of the system
State state{State::Stopped}; State state{State::Stopped};
/// Sample rate for the system /// Sample rate for the system
+12 -20
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -16,22 +16,22 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
SystemManager::SystemManager(Core::System& core_) SystemManager::SystemManager(Core::System& core_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {} : core{core_}
{}
SystemManager::~SystemManager() { SystemManager::~SystemManager() {
Stop(); Stop();
} }
void SystemManager::InitializeUnsafe() { void SystemManager::InitializeUnsafe() {
if (!active) { if (!thread.joinable()) {
active = true; core.AudioCore().ADSP().AudioRenderer().Start();
audio_renderer.Start();
thread = std::jthread([this](std::stop_token stop_token) { thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioRenderSystemManager"); Common::SetCurrentThreadName("AudioRenderSystemManager");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High); auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
while (active && !stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
{ {
std::scoped_lock l{mutex1}; std::scoped_lock lk{mutex};
for (auto system : systems) for (auto system : systems)
system->SendCommandToDsp(); system->SendCommandToDsp();
} }
@@ -43,39 +43,31 @@ void SystemManager::InitializeUnsafe() {
} }
void SystemManager::Stop() { void SystemManager::Stop() {
if (active) { if (thread.joinable()) {
active = false;
thread.request_stop(); thread.request_stop();
thread.join(); thread.join();
audio_renderer.Stop(); core.AudioCore().ADSP().AudioRenderer().Stop();
} }
} }
bool SystemManager::Add(System& system_) { bool SystemManager::Add(System& system_) {
std::scoped_lock l2{mutex2}; std::scoped_lock lk{mutex};
if (systems.size() + 1 > MaxRendererSessions) { if (systems.size() + 1 > MaxRendererSessions) {
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!"); LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
return false; return false;
} }
{
std::scoped_lock l{mutex1};
if (systems.empty()) if (systems.empty())
InitializeUnsafe(); InitializeUnsafe();
}
systems.push_back(&system_); systems.push_back(&system_);
return true; return true;
} }
bool SystemManager::Remove(System& system_) { bool SystemManager::Remove(System& system_) {
std::scoped_lock l2{mutex2}; std::scoped_lock lk{mutex};
{
std::scoped_lock l{mutex1};
if (systems.remove(&system_) == 0) { if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!"); LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false; return false;
} }
}
if (systems.empty()) if (systems.empty())
Stop(); Stop();
return true; return true;
+3 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -75,14 +75,8 @@ private:
std::list<System*> systems{}; std::list<System*> systems{};
/// Main worker thread for generating command lists /// Main worker thread for generating command lists
std::jthread thread; std::jthread thread;
/// Mutex for the systems /// Mutex for the systems list
std::mutex mutex1{}; std::mutex mutex{};
/// Mutex for adding/removing systems
std::mutex mutex2{};
/// Is the system manager thread active?
std::atomic<bool> active{};
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+7 -4
View File
@@ -35,19 +35,22 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
} }
static bool IsEOF(IPSFileType type, std::span<const u8> magic) { static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0) return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0); || (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
} }
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr) if (in == nullptr || ips == nullptr)
return nullptr; return nullptr;
auto in_data = in->ReadAllBytes(); const auto type = IdentifyMagic(ips->ReadBytes(0x5));
auto const type = IdentifyMagic(in_data);
if (type == IPSFileType::Error) if (type == IPSFileType::Error)
return nullptr; return nullptr;
auto in_data = in->ReadAllBytes();
if (in_data.size() == 0)
return nullptr;
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header u64 offset = 5; // After header
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
+8 -14
View File
@@ -9,7 +9,6 @@
#include "common/logging.h" #include "common/logging.h"
#include "common/uuid.h" #include "common/uuid.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
@@ -62,24 +61,17 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
SaveDataFactory::~SaveDataFactory() = default; SaveDataFactory::~SaveDataFactory() = default;
std::string SaveDataFactory::GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const {
if (type == SaveDataType::Account || type == SaveDataType::Device) {
const auto requested_id = title_id != 0 ? title_id : program_id;
const auto parent_id = system.GetContentProvider().GetParentApplicationId(requested_id);
title_id = parent_id.value_or(requested_id);
}
return GetFullPath(program_id, dir, space, type, title_id, user_id, save_id);
}
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const { VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id); const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
return dir->CreateDirectoryRelative(save_directory); return dir->CreateDirectoryRelative(save_directory);
} }
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id); const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory); auto out = dir->GetDirectoryRelative(save_directory);
@@ -162,7 +154,8 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const { u128 user_id) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0); const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -180,7 +173,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const { SaveDataSize new_value) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0); const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
-1
View File
@@ -50,7 +50,6 @@ public:
void SetAutoCreate(bool state); void SetAutoCreate(bool state);
private: private:
std::string GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const;
Core::System& system; Core::System& system;
ProgramId program_id; ProgramId program_id;
VirtualDir dir; VirtualDir dir;
+2 -2
View File
@@ -97,7 +97,7 @@ Result IAudioDevice::SetAudioDeviceOutputVolumeAuto(
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume); LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume);
if (device_name == "AudioTvOutput") { if (device_name == "AudioTvOutput") {
impl->SetDeviceVolumes(volume); impl->SetDeviceVolumes(system, volume);
} }
R_SUCCEED(); R_SUCCEED();
@@ -112,7 +112,7 @@ Result IAudioDevice::GetAudioDeviceOutputVolumeAuto(
*out_volume = 1.0f; *out_volume = 1.0f;
if (device_name == "AudioTvOutput") { if (device_name == "AudioTvOutput") {
*out_volume = impl->GetDeviceVolume(device_name); *out_volume = impl->GetDeviceVolume(system, device_name);
} }
R_SUCCEED(); R_SUCCEED();
+1 -1
View File
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioIn::~IAudioIn() { IAudioIn::~IAudioIn() {
impl->Free(); impl->Free(system);
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -65,7 +68,7 @@ Result IAudioInManager::OpenAudioInAuto(
Result IAudioInManager::ListAudioInsAutoFiltered( Result IAudioInManager::ListAudioInsAutoFiltered(
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) { OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
*out_count = impl->GetDeviceNames(out_audio_ins, true); *out_count = impl->GetDeviceNames(system, out_audio_ins, true);
R_SUCCEED(); R_SUCCEED();
} }
@@ -90,8 +93,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager()); R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(new_session_id)); R_TRY(impl->AcquireSessionId(system, new_session_id));
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
+1 -1
View File
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
impl->Free(); impl->Free(system);
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
} }
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager()); R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(new_session_id)); R_TRY(impl->AcquireSessionId(system, new_session_id));
const auto device_name = Common::StringFromBuffer(name[0].name); const auto device_name = Common::StringFromBuffer(name[0].name);
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
@@ -24,29 +24,65 @@
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
// params = { this, vaddr, bitsize }
code.LDR(X0, l_this); code.LDR(X0, l_this);
// X1 = vaddr
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0); code.BR(Xscratch0);
code.align(8); code.align(8);
code.l(l_this); code.l(l_this);
code.dx(info.this_ptr); code.dx(info.this_ptr);
code.l(l_addr); code.l(l_addr);
code.dx(info.fn_ptr); code.dx(info.fn_ptr);
return target; return target;
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
// params = { this, vaddr, value, bitsize }
code.LDR(X0, l_this);
// X1 = vaddr
// X2 = value
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
code.LDR(X0, l_this);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -59,6 +95,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
ABI_PushRegisters(code, save_regs, 0); ABI_PushRegisters(code, save_regs, 0);
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
code.MOV(Xscratch0, X0); code.MOV(Xscratch0, X0);
@@ -101,7 +138,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -115,6 +152,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, Xscratch1); code.MOV(X2, Xscratch1);
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
ABI_PopRegisters(code, save_regs, 0); ABI_PopRegisters(code, save_regs, 0);
@@ -176,14 +214,27 @@ void A32AddressSpace::EmitPrelude() {
UnprotectCodeMemory(); UnprotectCodeMemory();
prelude_info.read_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks);
prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks); prelude_info.read_memory_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf); prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf); prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf); prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf); prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.write_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf); prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
@@ -23,29 +23,65 @@
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this; oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>(); void* target = code.xptr<void*>();
// params = { this, vaddr, bitsize }
code.LDR(X0, l_this); code.LDR(X0, l_this);
// X1 = vaddr
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0); code.BR(Xscratch0);
code.align(8); code.align(8);
code.l(l_this); code.l(l_this);
code.dx(info.this_ptr); code.dx(info.this_ptr);
code.l(l_addr); code.l(l_addr);
code.dx(info.fn_ptr); code.dx(info.fn_ptr);
return target; return target;
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
// params = { this, vaddr, value, bitsize }
code.LDR(X0, l_this);
// X1 = vaddr
// X2 = value
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
oaknut::Label l_addr, l_this;
void* target = code.xptr<void*>();
code.LDR(X0, l_this);
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
code.align(8);
code.l(l_this);
code.dx(info.this_ptr);
code.l(l_addr);
code.dx(info.fn_ptr);
return target;
}
template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -58,6 +94,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
ABI_PushRegisters(code, save_regs, 0); ABI_PushRegisters(code, save_regs, 0);
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
code.MOV(Xscratch0, X0); code.MOV(Xscratch0, X0);
@@ -100,7 +137,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
} }
template<auto mfp, typename T> template<auto mfp, typename T>
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) { static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
using namespace oaknut::util; using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_); const auto info = Devirtualize<mfp>(this_);
@@ -114,6 +151,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
code.LDR(X0, l_this); code.LDR(X0, l_this);
code.MOV(X1, Xscratch0); code.MOV(X1, Xscratch0);
code.MOV(X2, Xscratch1); code.MOV(X2, Xscratch1);
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr); code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0); code.BLR(Xscratch0);
ABI_PopRegisters(code, save_regs, 0); ABI_PopRegisters(code, save_regs, 0);
@@ -342,18 +380,30 @@ void A64AddressSpace::EmitPrelude() {
UnprotectCodeMemory(); UnprotectCodeMemory();
prelude_info.read_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks); prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks); prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks); prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks); prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf); prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf); prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf); prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf); prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf); prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
prelude_info.write_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks); prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks); prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
@@ -147,44 +147,37 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
break; break;
// { this, vaddr, size } // { this, vaddr, size }
case LinkTarget::ReadMemory8: case LinkTarget::ReadMemory8:
c.LDR(X2, 1); c.BL(prelude_info.read_memory_8);
c.BL(prelude_info.read_memory);
break; break;
case LinkTarget::ReadMemory16: case LinkTarget::ReadMemory16:
c.LDR(X2, 2); c.BL(prelude_info.read_memory_16);
c.BL(prelude_info.read_memory);
break; break;
case LinkTarget::ReadMemory32: case LinkTarget::ReadMemory32:
c.LDR(X2, 4); c.BL(prelude_info.read_memory_32);
c.BL(prelude_info.read_memory);
break; break;
case LinkTarget::ReadMemory64: case LinkTarget::ReadMemory64:
c.LDR(X2, 8); c.BL(prelude_info.read_memory_64);
c.BL(prelude_info.read_memory);
break; break;
case LinkTarget::ReadMemory128: case LinkTarget::ReadMemory128:
c.BL(prelude_info.read_memory_128); c.BL(prelude_info.read_memory_128);
break; break;
// { this, vaddr, size } // { this, vaddr, size }
case LinkTarget::WrappedReadMemory8: case LinkTarget::WrappedReadMemory8:
c.LDR(X2, 1); c.BL(prelude_info.wrapped_read_memory_8);
c.BL(prelude_info.wrapped_read_memory);
break; break;
case LinkTarget::WrappedReadMemory16: case LinkTarget::WrappedReadMemory16:
c.LDR(X2, 2); c.BL(prelude_info.wrapped_read_memory_16);
c.BL(prelude_info.wrapped_read_memory);
break; break;
case LinkTarget::WrappedReadMemory32: case LinkTarget::WrappedReadMemory32:
c.LDR(X2, 4); c.BL(prelude_info.wrapped_read_memory_32);
c.BL(prelude_info.wrapped_read_memory);
break; break;
case LinkTarget::WrappedReadMemory64: case LinkTarget::WrappedReadMemory64:
c.LDR(X2, 8); c.BL(prelude_info.wrapped_read_memory_64);
c.BL(prelude_info.wrapped_read_memory);
break; break;
case LinkTarget::WrappedReadMemory128: case LinkTarget::WrappedReadMemory128:
c.BL(prelude_info.wrapped_read_memory_128); c.BL(prelude_info.wrapped_read_memory_128);
break; break;
//
case LinkTarget::ExclusiveReadMemory8: case LinkTarget::ExclusiveReadMemory8:
c.BL(prelude_info.exclusive_read_memory_8); c.BL(prelude_info.exclusive_read_memory_8);
break; break;
@@ -202,43 +195,37 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
break; break;
// { this, vaddr, value, size } // { this, vaddr, value, size }
case LinkTarget::WriteMemory8: case LinkTarget::WriteMemory8:
c.LDR(X3, 1); c.BL(prelude_info.write_memory_8);
c.BL(prelude_info.write_memory);
break; break;
case LinkTarget::WriteMemory16: case LinkTarget::WriteMemory16:
c.LDR(X3, 2); c.BL(prelude_info.write_memory_16);
c.BL(prelude_info.write_memory);
break; break;
case LinkTarget::WriteMemory32: case LinkTarget::WriteMemory32:
c.LDR(X3, 4); c.BL(prelude_info.write_memory_32);
c.BL(prelude_info.write_memory);
break; break;
case LinkTarget::WriteMemory64: case LinkTarget::WriteMemory64:
c.LDR(X3, 8); c.BL(prelude_info.write_memory_64);
c.BL(prelude_info.write_memory);
break; break;
case LinkTarget::WriteMemory128: case LinkTarget::WriteMemory128:
c.BL(prelude_info.write_memory_128); c.BL(prelude_info.write_memory_128);
break; break;
//
case LinkTarget::WrappedWriteMemory8: case LinkTarget::WrappedWriteMemory8:
c.LDR(X3, 1); c.BL(prelude_info.wrapped_write_memory_8);
c.BL(prelude_info.wrapped_write_memory);
break; break;
case LinkTarget::WrappedWriteMemory16: case LinkTarget::WrappedWriteMemory16:
c.LDR(X3, 2); c.BL(prelude_info.wrapped_write_memory_16);
c.BL(prelude_info.wrapped_write_memory);
break; break;
case LinkTarget::WrappedWriteMemory32: case LinkTarget::WrappedWriteMemory32:
c.LDR(X3, 4); c.BL(prelude_info.wrapped_write_memory_32);
c.BL(prelude_info.wrapped_write_memory);
break; break;
case LinkTarget::WrappedWriteMemory64: case LinkTarget::WrappedWriteMemory64:
c.LDR(X3, 8); c.BL(prelude_info.wrapped_write_memory_64);
c.BL(prelude_info.wrapped_write_memory);
break; break;
case LinkTarget::WrappedWriteMemory128: case LinkTarget::WrappedWriteMemory128:
c.BL(prelude_info.wrapped_write_memory_128); c.BL(prelude_info.wrapped_write_memory_128);
break; break;
//
case LinkTarget::ExclusiveWriteMemory8: case LinkTarget::ExclusiveWriteMemory8:
c.BL(prelude_info.exclusive_write_memory_8); c.BL(prelude_info.exclusive_write_memory_8);
break; break;
@@ -93,18 +93,30 @@ protected:
void* return_to_dispatcher; void* return_to_dispatcher;
void* return_from_run_code; void* return_from_run_code;
void* read_memory; void* read_memory_8;
void* read_memory_16;
void* read_memory_32;
void* read_memory_64;
void* read_memory_128; void* read_memory_128;
void* wrapped_read_memory; void* wrapped_read_memory_8;
void* wrapped_read_memory_16;
void* wrapped_read_memory_32;
void* wrapped_read_memory_64;
void* wrapped_read_memory_128; void* wrapped_read_memory_128;
void* exclusive_read_memory_8; void* exclusive_read_memory_8;
void* exclusive_read_memory_16; void* exclusive_read_memory_16;
void* exclusive_read_memory_32; void* exclusive_read_memory_32;
void* exclusive_read_memory_64; void* exclusive_read_memory_64;
void* exclusive_read_memory_128; void* exclusive_read_memory_128;
void* write_memory; void* write_memory_8;
void* write_memory_16;
void* write_memory_32;
void* write_memory_64;
void* write_memory_128; void* write_memory_128;
void* wrapped_write_memory; void* wrapped_write_memory_8;
void* wrapped_write_memory_16;
void* wrapped_write_memory_32;
void* wrapped_write_memory_64;
void* wrapped_write_memory_128; void* wrapped_write_memory_128;
void* exclusive_write_memory_8; void* exclusive_write_memory_8;
void* exclusive_write_memory_16; void* exclusive_write_memory_16;
+17 -3
View File
@@ -761,6 +761,9 @@ void EmulatedController::StartMotionCalibration() {
} }
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) { void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
if (index >= controller.button_values.size()) { if (index >= controller.button_values.size()) {
return; return;
} }
@@ -913,10 +916,21 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
break; break;
} }
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); if (!is_connected) {
const auto& player = Settings::values.players.GetValue()[player_index]; if (npad_type == NpadStyleIndex::Handheld) {
if (player.connected) { if (npad_id_type == NpadIdType::Handheld) {
Connect(); Connect();
controller_connected[player_index] = true;
}
} else if (npad_type != NpadStyleIndex::Handheld) {
if (npad_id_type == NpadIdType::Player1) {
Connect();
controller_connected[player_index] = true;
} else if (player.connected && !controller_connected[player_index]) {
Connect();
controller_connected[player_index] = true;
}
}
} }
TriggerOnChange(ControllerTriggerType::Button, true); TriggerOnChange(ControllerTriggerType::Button, true);
@@ -22,6 +22,7 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "hid_core/frontend/motion_input.h" #include "hid_core/frontend/motion_input.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
@@ -584,6 +585,7 @@ private:
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE, std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
DEFAULT_VIBRATION_VALUE}; DEFAULT_VIBRATION_VALUE};
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{}; std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
std::array<bool, HIDCore::available_controllers> controller_connected{};
// Atomically synched values // Atomically synched values
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None}; std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};