mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-07 22:29:48 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fe88221d1 | |||
| e9bfd97c7f | |||
| 773c29ed85 | |||
| 09f05f0313 | |||
| 3b03ade8d8 | |||
| 78cacac308 | |||
| 84f43f2c63 |
@@ -333,11 +333,6 @@
|
|||||||
"repo": "herumi/xbyak",
|
"repo": "herumi/xbyak",
|
||||||
"version": "v7.40.1"
|
"version": "v7.40.1"
|
||||||
},
|
},
|
||||||
"zbic": {
|
|
||||||
"hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482",
|
|
||||||
"repo": "kinnay/zbic",
|
|
||||||
"version": "11b08f2712264bbed731545085cbd9702096ceb7"
|
|
||||||
},
|
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||||
"min_version": "1.2",
|
"min_version": "1.2",
|
||||||
|
|||||||
+21
-33
@@ -1,44 +1,32 @@
|
|||||||
# User Handbook - Command Line
|
# User Handbook - Command Line
|
||||||
|
|
||||||
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments.
|
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
|
||||||
|
|
||||||
|
## eden
|
||||||
|
|
||||||
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
||||||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
||||||
- `--config/-c`: Specify alternate configuration file.
|
- `-f`: Use fullscreen.
|
||||||
- `--fullscreen/-f`: Use fullscreen.
|
- `-u <number>`: Select the index of the user to load as.
|
||||||
- `--help/-h`: Display help.
|
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
|
||||||
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
|
||||||
- `--multiplayer/-m`: Specify multiplayer options.
|
|
||||||
- `--program/-p`: Specify the program arguments to pass (optional).
|
|
||||||
- `--user/-u <number>`: Specify the user index.
|
|
||||||
- `--version/-v`: Display version and quit.
|
|
||||||
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
|
|
||||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
|
||||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
|
||||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
|
||||||
|
|
||||||
Only the Qt frontend supports the following arguments:
|
|
||||||
|
|
||||||
- `-qlaunch`: Launch QLaunch.
|
- `-qlaunch`: Launch QLaunch.
|
||||||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
||||||
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
||||||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
||||||
- `-setup`: Launch setup applet.
|
- `-setup`: Launch setup applet.
|
||||||
|
|
||||||
`eden` (provided with `--room`), and `eden-room` supports the following options as well:
|
## eden-cli
|
||||||
|
|
||||||
- `-n/--room-name`: The name of the room.
|
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
||||||
- `-d/--room-description`: The room description.
|
- `--config/-c`: Specify alternate configuration file.
|
||||||
- `-s/--bind-address`: The bind address for the room.
|
- `--fullscreen/-f`: Set fullscreen.
|
||||||
- `-p/--port`: The port used for the room.
|
- `--help/-h`: Display help.
|
||||||
- `-m/--max-members`: The maximum number of players for this room.
|
- `--game/-g`: Specify the game to run.
|
||||||
- `-w/--password`: The password for the room.
|
- `--multiplayer/-m`: Specify multiplayer options.
|
||||||
- `-g/--preferred-game`: The preferred game for this room.
|
- `--program/-p`: Specify the program arguments to pass (optional).
|
||||||
- `-i/--preferred-game-id`: The preferred game-id for this room.
|
- `--user/-u`: Specify the user index.
|
||||||
- `-u/--username`: The username used for announce.
|
- `--version/-v`: Display version and quit.
|
||||||
- `-t/--token`: The token used for announce.
|
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
||||||
- `-a/--web-api-url`: yuzu Web API url.
|
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
||||||
- `-b/--ban-list-file`: The file for storing the room ban list.
|
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
||||||
- `-l/--log-file`: The file for storing the room log.
|
- `--singlecore/-s`: Forces single-core regardless of settings.
|
||||||
- `-h/--help`: Display this help and exit.
|
|
||||||
- `-v/--version`: Output version information and exit.
|
|
||||||
|
|||||||
Vendored
-5
@@ -48,11 +48,6 @@ if (NOT TARGET stb::headers)
|
|||||||
add_library(stb::headers ALIAS stb)
|
add_library(stb::headers ALIAS stb)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
AddJsonPackage(NAME zbic DOWNLOAD_ONLY)
|
|
||||||
set(ZBIC_INCLUDE_DIR
|
|
||||||
"${zbic_SOURCE_DIR}/src"
|
|
||||||
PARENT_SCOPE)
|
|
||||||
|
|
||||||
# ItaniumDemangle (Windows only)
|
# ItaniumDemangle (Windows only)
|
||||||
if (WIN32 AND NOT TARGET LLVM::Demangle)
|
if (WIN32 AND NOT TARGET LLVM::Demangle)
|
||||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||||
|
|||||||
+1
@@ -30,6 +30,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
|
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||||
RENDERER_DEBUG("debug"),
|
RENDERER_DEBUG("debug"),
|
||||||
|
|||||||
+7
@@ -908,6 +908,13 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.enable_buffer_history_description
|
descriptionId = R.string.enable_buffer_history_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||||
|
titleId = R.string.enable_gpu_buffer_readback,
|
||||||
|
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||||
|
)
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||||
|
|||||||
+1
@@ -549,6 +549,7 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||||
|
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.hacks))
|
add(HeaderSetting(R.string.hacks))
|
||||||
|
|||||||
@@ -580,6 +580,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||||
|
|
||||||
@@ -1097,6 +1099,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">صارم</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">محافظ</string>
|
<string name="vram_usage_conservative">محافظ</string>
|
||||||
<string name="vram_usage_aggressive">عدواني</string>
|
<string name="vram_usage_aggressive">عدواني</string>
|
||||||
|
|||||||
@@ -967,6 +967,7 @@ Wirklich fortfahren?</string>
|
|||||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Strikt</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Konservativ</string>
|
<string name="vram_usage_conservative">Konservativ</string>
|
||||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||||
|
|||||||
@@ -524,6 +524,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||||
|
|
||||||
@@ -1036,6 +1038,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Estricto</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Conservador</string>
|
<string name="vram_usage_conservative">Conservador</string>
|
||||||
<string name="vram_usage_aggressive">Agresivo</string>
|
<string name="vram_usage_aggressive">Agresivo</string>
|
||||||
|
|||||||
@@ -569,6 +569,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||||
|
|
||||||
@@ -1086,6 +1088,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Строгий</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Консервативный</string>
|
<string name="vram_usage_conservative">Консервативный</string>
|
||||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||||
|
|||||||
@@ -570,6 +570,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||||
|
|
||||||
@@ -1087,6 +1089,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">严格</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">保守式</string>
|
<string name="vram_usage_conservative">保守式</string>
|
||||||
<string name="vram_usage_aggressive">主动式</string>
|
<string name="vram_usage_aggressive">主动式</string>
|
||||||
|
|||||||
@@ -561,6 +561,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
||||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||||
|
|
||||||
|
|||||||
@@ -558,6 +558,7 @@
|
|||||||
<item>@string/gpu_fence_behavior_immediate</item>
|
<item>@string/gpu_fence_behavior_immediate</item>
|
||||||
<item>@string/gpu_fence_behavior_balanced</item>
|
<item>@string/gpu_fence_behavior_balanced</item>
|
||||||
<item>@string/gpu_fence_behavior_accurate</item>
|
<item>@string/gpu_fence_behavior_accurate</item>
|
||||||
|
<item>@string/gpu_fence_behavior_strict</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="gpuFenceBehaviorValues">
|
<integer-array name="gpuFenceBehaviorValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|||||||
@@ -586,6 +586,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||||
<string name="enable_buffer_history">Enable buffer history</string>
|
<string name="enable_buffer_history">Enable buffer history</string>
|
||||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||||
|
|
||||||
@@ -1136,6 +1138,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Strict</string>
|
||||||
|
|
||||||
<!-- ASTC Decoding Method Choices -->
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -19,11 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSP::Shutdown() {
|
|
||||||
audio_renderer->Stop();
|
|
||||||
opus_decoder->Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
|
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
|
||||||
return *audio_renderer;
|
return *audio_renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -45,7 +45,6 @@ public:
|
|||||||
explicit ADSP(Core::System& system, Sink::Sink& sink);
|
explicit ADSP(Core::System& system, Sink::Sink& sink);
|
||||||
~ADSP() = default;
|
~ADSP() = default;
|
||||||
|
|
||||||
void Shutdown();
|
|
||||||
AudioRenderer::AudioRenderer& AudioRenderer();
|
AudioRenderer::AudioRenderer& AudioRenderer();
|
||||||
OpusDecoder::OpusDecoder& OpusDecoder();
|
OpusDecoder::OpusDecoder& OpusDecoder();
|
||||||
|
|
||||||
|
|||||||
@@ -34,19 +34,26 @@ void AudioRenderer::Start() {
|
|||||||
|
|
||||||
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
||||||
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
||||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
|
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||||
|
"message response from ADSP!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Stop() {
|
void AudioRenderer::Stop() {
|
||||||
if (main_thread.joinable()) {
|
if (!running) {
|
||||||
main_thread.request_stop();
|
return;
|
||||||
mailbox.Send(Direction::DSP, Message::Shutdown);
|
|
||||||
if (mailbox.Receive(Direction::Host, main_thread.get_stop_token()) != Message::Shutdown) {
|
|
||||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
|
|
||||||
}
|
|
||||||
main_thread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mailbox.Send(Direction::DSP, Message::Shutdown);
|
||||||
|
if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
|
||||||
|
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||||
|
"message response from ADSP!");
|
||||||
|
}
|
||||||
|
main_thread.request_stop();
|
||||||
|
main_thread.join();
|
||||||
|
|
||||||
for (auto& stream : streams) {
|
for (auto& stream : streams) {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream->Stop();
|
stream->Stop();
|
||||||
@@ -54,6 +61,7 @@ void AudioRenderer::Stop() {
|
|||||||
stream = nullptr;
|
stream = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Signal() {
|
void AudioRenderer::Signal() {
|
||||||
@@ -61,12 +69,13 @@ void AudioRenderer::Signal() {
|
|||||||
Send(Direction::DSP, Message::Render);
|
Send(Direction::DSP, Message::Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Wait(std::stop_token stop_token) {
|
void AudioRenderer::Wait() {
|
||||||
auto msg = mailbox.Receive(Direction::Host, stop_token);
|
auto msg = Receive(Direction::Host);
|
||||||
if (msg != Message::RenderResponse) {
|
if (msg != Message::RenderResponse) {
|
||||||
LOG_ERROR(Service_Audio,
|
LOG_ERROR(Service_Audio,
|
||||||
"Did not receive the expected render response from the AudioRenderer! Expected "
|
"Did not receive the expected render response from the AudioRenderer! Expected "
|
||||||
"{}, got {}", Message::RenderResponse, msg);
|
"{}, got {}",
|
||||||
|
Message::RenderResponse, msg);
|
||||||
}
|
}
|
||||||
PostDSPClearCommandBuffer();
|
PostDSPClearCommandBuffer();
|
||||||
}
|
}
|
||||||
@@ -138,9 +147,7 @@ void AudioRenderer::Main(std::stop_token stop_token) {
|
|||||||
constexpr u64 max_process_time{2'304'000ULL};
|
constexpr u64 max_process_time{2'304'000ULL};
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto msg = mailbox.Receive(Direction::DSP, stop_token);
|
auto msg{mailbox.Receive(Direction::DSP)};
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Message::Shutdown:
|
case Message::Shutdown:
|
||||||
mailbox.Send(Direction::Host, Message::Shutdown);
|
mailbox.Send(Direction::Host, Message::Shutdown);
|
||||||
|
|||||||
@@ -63,15 +63,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
void NotifyShutdown();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the AudioRenderer.
|
* Stop the AudioRenderer.
|
||||||
*/
|
*/
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
void Signal();
|
void Signal();
|
||||||
void Wait(std::stop_token stop_token);
|
void Wait();
|
||||||
|
|
||||||
void Send(Direction dir, u32 message);
|
void Send(Direction dir, u32 message);
|
||||||
u32 Receive(Direction dir);
|
u32 Receive(Direction dir);
|
||||||
@@ -104,6 +102,8 @@ private:
|
|||||||
Mailbox mailbox;
|
Mailbox mailbox;
|
||||||
/// Main thread
|
/// Main thread
|
||||||
std::jthread main_thread{};
|
std::jthread main_thread{};
|
||||||
|
/// The current state
|
||||||
|
std::atomic<bool> running{};
|
||||||
/// Shared memory of input command buffers, set by host, read by DSP
|
/// Shared memory of input command buffers, set by host, read by DSP
|
||||||
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
|
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
|
||||||
/// The command lists to process
|
/// The command lists to process
|
||||||
|
|||||||
@@ -41,16 +41,19 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
if (main_thread.joinable()) {
|
if (!running) {
|
||||||
// Shutdown the thread
|
|
||||||
Send(Direction::DSP, Message::Shutdown);
|
|
||||||
main_thread.request_stop();
|
|
||||||
auto msg = Receive(Direction::Host, main_thread.get_stop_token());
|
|
||||||
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
|
|
||||||
main_thread.join();
|
|
||||||
} else {
|
|
||||||
init_thread.request_stop();
|
init_thread.request_stop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown the thread
|
||||||
|
Send(Direction::DSP, Message::Shutdown);
|
||||||
|
auto msg = Receive(Direction::Host);
|
||||||
|
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
|
||||||
|
Message::ShutdownOK, msg);
|
||||||
|
main_thread.request_stop();
|
||||||
|
main_thread.join();
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Send(Direction dir, u32 message) {
|
void OpusDecoder::Send(Direction dir, u32 message) {
|
||||||
@@ -70,6 +73,7 @@ void OpusDecoder::Init(std::stop_token stop_token) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
||||||
|
running = true;
|
||||||
Send(Direction::Host, Message::StartOK);
|
Send(Direction::Host, Message::StartOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,15 +82,12 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto msg = Receive(Direction::DSP, stop_token);
|
auto msg = Receive(Direction::DSP, stop_token);
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Message::Shutdown:
|
case Shutdown:
|
||||||
Send(Direction::Host, Message::ShutdownOK);
|
Send(Direction::Host, Message::ShutdownOK);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case Message::GetWorkBufferSize: {
|
case GetWorkBufferSize: {
|
||||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
|
|
||||||
ASSERT(IsValidChannelCount(channel_count));
|
ASSERT(IsValidChannelCount(channel_count));
|
||||||
@@ -95,7 +96,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::InitializeDecodeObject: {
|
case InitializeDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
@@ -112,7 +113,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::ShutdownDecodeObject: {
|
case ShutdownDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::DecodeInterleaved: {
|
case DecodeInterleaved: {
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
@@ -160,19 +161,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::MapMemory: {
|
case MapMemory: {
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
Send(Direction::Host, Message::MapMemoryOK);
|
Send(Direction::Host, Message::MapMemoryOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::UnmapMemory: {
|
case UnmapMemory: {
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::GetWorkBufferSizeForMultiStream: {
|
case GetWorkBufferSizeForMultiStream: {
|
||||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
||||||
|
|
||||||
@@ -183,7 +184,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::InitializeMultiStreamDecodeObject: {
|
case InitializeMultiStreamDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
@@ -210,7 +211,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::ShutdownMultiStreamDecodeObject: {
|
case ShutdownMultiStreamDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::DecodeInterleavedForMultiStream: {
|
case DecodeInterleavedForMultiStream: {
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
@@ -265,15 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Shutdown() {
|
|
||||||
if (init_thread.joinable()) {
|
|
||||||
init_thread.request_stop();
|
|
||||||
init_thread.join(); //must explicitly wait for init to join
|
|
||||||
}
|
|
||||||
if (main_thread.joinable()) {
|
|
||||||
main_thread.request_stop();
|
|
||||||
Send(Direction::DSP, Message::Shutdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore::ADSP::OpusDecoder
|
} // namespace AudioCore::ADSP::OpusDecoder
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
~OpusDecoder();
|
~OpusDecoder();
|
||||||
|
|
||||||
bool IsRunning() const noexcept {
|
bool IsRunning() const noexcept {
|
||||||
return main_thread.joinable();
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Send(Direction dir, u32 message);
|
void Send(Direction dir, u32 message);
|
||||||
@@ -67,8 +67,6 @@ public:
|
|||||||
shared_memory = &shared_memory_;
|
shared_memory = &shared_memory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
||||||
@@ -87,6 +85,8 @@ private:
|
|||||||
std::jthread init_thread{};
|
std::jthread init_thread{};
|
||||||
/// Main thread
|
/// Main thread
|
||||||
std::jthread main_thread{};
|
std::jthread main_thread{};
|
||||||
|
/// The current state
|
||||||
|
bool running{};
|
||||||
/// Structure shared with the host, input data set by the host before sending a mailbox message,
|
/// Structure shared with the host, input data set by the host before sending a mailbox message,
|
||||||
/// and the responses are written back by the OpusDecoder.
|
/// and the responses are written back by the OpusDecoder.
|
||||||
SharedMemory* shared_memory{};
|
SharedMemory* shared_memory{};
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -33,7 +33,6 @@ void AudioCore::CreateSinks() {
|
|||||||
|
|
||||||
void AudioCore::Shutdown() {
|
void AudioCore::Shutdown() {
|
||||||
audio_manager->Shutdown();
|
audio_manager->Shutdown();
|
||||||
adsp->Shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager& AudioCore::GetAudioManager() {
|
AudioManager& AudioCore::GetAudioManager() {
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
|
|
||||||
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();
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
const auto timed_out = events.Wait(l, std::chrono::seconds{2});
|
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -35,19 +35,20 @@ AudioManager::AudioManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::Shutdown() {
|
void AudioManager::Shutdown() {
|
||||||
|
events.SetAudioEvent(Event::Type::Max, true);
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
events.SetAudioEvent(Event::Type::Max, true);
|
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,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,29 +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);
|
||||||
|
|
||||||
/// @brief 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.
|
||||||
@@ -71,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
|
||||||
|
|||||||
@@ -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};
|
||||||
}
|
for (auto& session : sessions) {
|
||||||
|
if (session) {
|
||||||
std::scoped_lock l{mutex};
|
session->StartSession();
|
||||||
for (auto& session : sessions) {
|
}
|
||||||
if (session) {
|
|
||||||
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
|
|||||||
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
|
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
|
||||||
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
|
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
|
||||||
|
|
||||||
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
|
if (in_use == buffer_params->in_use && !buffer_unmapped) {
|
||||||
error_info.error_code = ResultSuccess;
|
error_info.error_code = ResultSuccess;
|
||||||
error_info.address = CpuAddr(0);
|
error_info.address = CpuAddr(0);
|
||||||
out_status.writeOffset = current_state->last_pos2;
|
out_status.writeOffset = current_state->last_pos2;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -31,10 +31,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct DeviceInParameter {
|
struct DeviceInParameter {
|
||||||
/* 0x000 */ u8 name[0x100];
|
/* 0x000 */ char name[0x100];
|
||||||
/* 0x100 */ u32 input_count;
|
/* 0x100 */ u32 input_count;
|
||||||
/* 0x104 */ std::array<s8, MaxChannels> inputs;
|
/* 0x104 */ std::array<s8, MaxChannels> inputs;
|
||||||
/* 0x10A */ u8 unk10A[0x1];
|
/* 0x10A */ char unk10A[0x1];
|
||||||
/* 0x10B */ bool downmix_enabled;
|
/* 0x10B */ bool downmix_enabled;
|
||||||
/* 0x10C */ std::array<f32, 4> downmix_coeff;
|
/* 0x10C */ std::array<f32, 4> downmix_coeff;
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
struct DeviceState {
|
struct DeviceState {
|
||||||
/* 0x00 */ UpsamplerInfo* upsampler_info;
|
/* 0x00 */ UpsamplerInfo* upsampler_info;
|
||||||
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
|
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
|
||||||
/* 0x18 */ u8 unk18[0x18];
|
/* 0x18 */ char unk18[0x18];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
|
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ public:
|
|||||||
/* 0x14 */ u32 previous_pos;
|
/* 0x14 */ u32 previous_pos;
|
||||||
/* 0x18 */ SampleFormat format;
|
/* 0x18 */ SampleFormat format;
|
||||||
/* 0x1C */ std::array<s8, MaxChannels> inputs;
|
/* 0x1C */ std::array<s8, MaxChannels> inputs;
|
||||||
/* 0x22 */ u8 in_use;
|
/* 0x22 */ bool in_use;
|
||||||
/* 0x23 */ u8 unk23[0x5];
|
/* 0x23 */ char unk23[0x5];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CircularBufferInParameter) == 0x28,
|
static_assert(sizeof(CircularBufferInParameter) == 0x28,
|
||||||
"CircularBufferInParameter has the wrong size!");
|
"CircularBufferInParameter has the wrong size!");
|
||||||
@@ -65,16 +65,16 @@ public:
|
|||||||
/* 0x00 */ u32 last_pos2;
|
/* 0x00 */ u32 last_pos2;
|
||||||
/* 0x04 */ s32 current_pos;
|
/* 0x04 */ s32 current_pos;
|
||||||
/* 0x08 */ u32 last_pos;
|
/* 0x08 */ u32 last_pos;
|
||||||
/* 0x0C */ u8 unk0C[0x4];
|
/* 0x0C */ char unk0C[0x4];
|
||||||
/* 0x10 */ AddressInfo address_info;
|
/* 0x10 */ AddressInfo address_info;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
|
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
|
||||||
|
|
||||||
struct InParameter {
|
struct InParameter {
|
||||||
/* 0x000 */ Type type;
|
/* 0x000 */ Type type;
|
||||||
/* 0x001 */ u8 in_use;
|
/* 0x001 */ bool in_use;
|
||||||
/* 0x004 */ u32 node_id;
|
/* 0x004 */ u32 node_id;
|
||||||
/* 0x008 */ u8 unk08[0x18];
|
/* 0x008 */ char unk08[0x18];
|
||||||
union {
|
union {
|
||||||
/* 0x020 */ DeviceInParameter device;
|
/* 0x020 */ DeviceInParameter device;
|
||||||
/* 0x020 */ CircularBufferInParameter circular_buffer;
|
/* 0x020 */ CircularBufferInParameter circular_buffer;
|
||||||
@@ -84,7 +84,7 @@ public:
|
|||||||
|
|
||||||
struct OutStatus {
|
struct OutStatus {
|
||||||
/* 0x00 */ u32 writeOffset;
|
/* 0x00 */ u32 writeOffset;
|
||||||
/* 0x04 */ u8 unk04[0x1C];
|
/* 0x04 */ char unk04[0x1C];
|
||||||
}; // size == 0x20
|
}; // size == 0x20
|
||||||
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
|
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
|
||||||
|
|
||||||
@@ -162,18 +162,19 @@ public:
|
|||||||
u8* GetParameter();
|
u8* GetParameter();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Type of this sink
|
||||||
|
Type type{Type::Invalid};
|
||||||
|
/// Is this sink in use?
|
||||||
|
bool in_use{};
|
||||||
|
/// Is this sink's buffer unmapped? Circular only
|
||||||
|
bool buffer_unmapped{};
|
||||||
|
/// Node id for this sink
|
||||||
|
u32 node_id{};
|
||||||
/// State buffer for this sink
|
/// State buffer for this sink
|
||||||
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
|
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
|
||||||
/// Parameter buffer for this sink
|
/// Parameter buffer for this sink
|
||||||
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{};
|
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
|
||||||
/// Type of this sink
|
parameter{};
|
||||||
Type type{Type::Invalid};
|
|
||||||
/// Node id for this sink
|
|
||||||
u32 node_id{};
|
|
||||||
/// Is this sink in use?
|
|
||||||
bool in_use : 1 = false;
|
|
||||||
/// Is this sink's buffer unmapped? Circular only
|
|
||||||
bool buffer_unmapped : 1 = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -479,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, IsActive(), 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;
|
||||||
@@ -586,11 +585,11 @@ bool System::IsActive() const {
|
|||||||
|
|
||||||
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 (IsActive()) {
|
if (IsActive()) {
|
||||||
terminate_event.Reset();
|
terminate_event.Reset();
|
||||||
const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id);
|
const auto remaining_command_count = audio_renderer.GetRemainCommandCount(session_id);
|
||||||
u64 command_size{0};
|
u64 command_size{0};
|
||||||
|
|
||||||
if (remaining_command_count) {
|
if (remaining_command_count) {
|
||||||
@@ -617,17 +616,16 @@ void System::SendCommandToDsp() {
|
|||||||
auto time_limit{
|
auto time_limit{
|
||||||
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
||||||
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
||||||
core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id,
|
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
|
||||||
translated_addr, command_size,
|
applet_resource_user_id, process_handle,
|
||||||
time_limit, applet_resource_user_id,
|
reset_command_buffers);
|
||||||
process_handle, reset_command_buffers);
|
|
||||||
reset_command_buffers = false;
|
reset_command_buffers = false;
|
||||||
command_buffer_size = command_size;
|
command_buffer_size = command_size;
|
||||||
if (remaining_command_count == 0) {
|
if (remaining_command_count == 0) {
|
||||||
adsp_rendered_event->Signal(core.Kernel());
|
adsp_rendered_event->Signal(core.Kernel());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id);
|
audio_renderer.ClearRemainCommandCount(session_id);
|
||||||
terminate_event.Set();
|
terminate_event.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,8 +224,6 @@ private:
|
|||||||
Core::System& core;
|
Core::System& core;
|
||||||
/// 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
|
||||||
|
|||||||
@@ -11,16 +11,13 @@
|
|||||||
#include "audio_core/renderer/system_manager.h"
|
#include "audio_core/renderer/system_manager.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/kernel/k_scheduler.h"
|
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
SystemManager::SystemManager(Core::System& core_)
|
SystemManager::SystemManager(Core::System& core_)
|
||||||
: core{core_}
|
: core{core_}
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
SystemManager::~SystemManager() {
|
SystemManager::~SystemManager() {
|
||||||
Stop();
|
Stop();
|
||||||
@@ -29,9 +26,9 @@ SystemManager::~SystemManager() {
|
|||||||
void SystemManager::InitializeUnsafe() {
|
void SystemManager::InitializeUnsafe() {
|
||||||
if (!thread.joinable()) {
|
if (!thread.joinable()) {
|
||||||
core.AudioCore().ADSP().AudioRenderer().Start();
|
core.AudioCore().ADSP().AudioRenderer().Start();
|
||||||
thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() {
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
||||||
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||||
auto const stop_token = thread.get_stop_token();
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
@@ -39,30 +36,29 @@ void SystemManager::InitializeUnsafe() {
|
|||||||
system->SendCommandToDsp();
|
system->SendCommandToDsp();
|
||||||
}
|
}
|
||||||
audio_renderer.Signal();
|
audio_renderer.Signal();
|
||||||
audio_renderer.Wait(stop_token);
|
audio_renderer.Wait();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::Stop() {
|
void SystemManager::Stop() {
|
||||||
core.AudioCore().ADSP().AudioRenderer().Stop();
|
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
thread.join();
|
thread.join();
|
||||||
|
core.AudioCore().ADSP().AudioRenderer().Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Add(System& system_) {
|
bool SystemManager::Add(System& system_) {
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
if (systems.size() >= 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;
|
||||||
}
|
}
|
||||||
systems.push_back(&system_);
|
if (systems.empty())
|
||||||
if (!systems.empty()) {
|
|
||||||
InitializeUnsafe();
|
InitializeUnsafe();
|
||||||
}
|
systems.push_back(&system_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +68,8 @@ bool SystemManager::Remove(System& system_) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ 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 reading and adding/removing systems
|
/// Mutex for the systems list
|
||||||
std::mutex mutex{};
|
std::mutex mutex{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ add_library(
|
|||||||
logging.h
|
logging.h
|
||||||
lz4_compression.cpp
|
lz4_compression.cpp
|
||||||
lz4_compression.h
|
lz4_compression.h
|
||||||
|
make_unique_for_overwrite.h
|
||||||
math_util.h
|
math_util.h
|
||||||
memory_detect.cpp
|
memory_detect.cpp
|
||||||
memory_detect.h
|
memory_detect.h
|
||||||
@@ -137,8 +138,6 @@ add_library(
|
|||||||
uuid.cpp
|
uuid.cpp
|
||||||
uuid.h
|
uuid.h
|
||||||
vector_math.h
|
vector_math.h
|
||||||
zbic_compression.cpp
|
|
||||||
zbic_compression.h
|
|
||||||
zstd_compression.cpp
|
zstd_compression.cpp
|
||||||
zstd_compression.h
|
zstd_compression.h
|
||||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||||
@@ -149,10 +148,6 @@ add_library(
|
|||||||
net/net.h net/net.cpp
|
net/net.h net/net.cpp
|
||||||
container/unordered_map.h container/unordered_set.h)
|
container/unordered_map.h container/unordered_set.h)
|
||||||
|
|
||||||
set_source_files_properties(zbic_compression.cpp PROPERTIES
|
|
||||||
INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}"
|
|
||||||
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:Clang,GNU>:-Wno-unused-function;-Wno-missing-declarations;-Wno-shadow>")
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_sources(common PRIVATE windows/timer_resolution.cpp
|
target_sources(common PRIVATE windows/timer_resolution.cpp
|
||||||
windows/timer_resolution.h)
|
windows/timer_resolution.h)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -109,6 +109,12 @@ private:
|
|||||||
if (read_index == producer.index.load(std::memory_order::acquire)) {
|
if (read_index == producer.index.load(std::memory_order::acquire)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if constexpr (Mode == PopMode::Wait) {
|
||||||
|
// Wait until the queue is not empty.
|
||||||
|
std::unique_lock lock{consumer.cv_mutex};
|
||||||
|
consumer.cv.wait(lock, [this, read_index] {
|
||||||
|
return read_index != producer.index.load(std::memory_order::acquire);
|
||||||
|
});
|
||||||
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
||||||
// Wait until the queue is not empty.
|
// Wait until the queue is not empty.
|
||||||
std::unique_lock lock{consumer.cv_mutex};
|
std::unique_lock lock{consumer.cv_mutex};
|
||||||
@@ -118,12 +124,6 @@ private:
|
|||||||
if (stop_token.stop_requested()) {
|
if (stop_token.stop_requested()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if constexpr (Mode == PopMode::Wait) {
|
|
||||||
// Wait until the queue is not empty.
|
|
||||||
std::unique_lock lock{consumer.cv_mutex};
|
|
||||||
consumer.cv.wait(lock, [this, read_index] {
|
|
||||||
return read_index != producer.index.load(std::memory_order::acquire);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
requires(!std::is_array_v<T>)
|
||||||
|
std::unique_ptr<T> make_unique_for_overwrite() {
|
||||||
|
return std::unique_ptr<T>(new T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
requires std::is_unbounded_array_v<T>
|
||||||
|
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
|
||||||
|
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class... Args>
|
||||||
|
requires std::is_bounded_array_v<T>
|
||||||
|
void make_unique_for_overwrite(Args&&...) = delete;
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
|
||||||
|
#include "common/make_unique_for_overwrite.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -37,9 +38,8 @@ public:
|
|||||||
ScratchBuffer() = default;
|
ScratchBuffer() = default;
|
||||||
|
|
||||||
explicit ScratchBuffer(size_type initial_capacity)
|
explicit ScratchBuffer(size_type initial_capacity)
|
||||||
: last_requested_size{initial_capacity}
|
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
|
||||||
, buffer_capacity{initial_capacity}
|
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
||||||
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
|
||||||
|
|
||||||
~ScratchBuffer() = default;
|
~ScratchBuffer() = default;
|
||||||
ScratchBuffer(const ScratchBuffer&) = delete;
|
ScratchBuffer(const ScratchBuffer&) = delete;
|
||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
/// The previously held data will remain intact.
|
/// The previously held data will remain intact.
|
||||||
void resize(size_type size) {
|
void resize(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
auto new_buffer = std::make_unique_for_overwrite<T[]>(size);
|
auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
|
||||||
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
||||||
buffer = std::move(new_buffer);
|
buffer = std::move(new_buffer);
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
void resize_destructive(size_type size) {
|
void resize_destructive(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
|
buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||||
}
|
}
|
||||||
last_requested_size = size;
|
last_requested_size = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,10 @@ bool IsGPUFenceBehaviorAccurate() {
|
|||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsGPUFenceBehaviorStrict() {
|
||||||
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsFastmemEnabled() {
|
bool IsFastmemEnabled() {
|
||||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||||
return bool(values.cpuopt_fastmem);
|
return bool(values.cpuopt_fastmem);
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ struct Values {
|
|||||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Accurate,
|
GpuFenceBehavior::Strict,
|
||||||
"gpu_fence_behavior",
|
"gpu_fence_behavior",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
@@ -655,6 +655,13 @@ struct Values {
|
|||||||
|
|
||||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||||
|
false,
|
||||||
|
"enable_gpu_buffer_readback",
|
||||||
|
Category::RendererAdvanced,
|
||||||
|
Specialization::Default,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
@@ -972,6 +979,7 @@ bool IsDMALevelSafe();
|
|||||||
bool IsGPUFenceBehaviorDefault();
|
bool IsGPUFenceBehaviorDefault();
|
||||||
bool IsGPUFenceBehaviorBalanced();
|
bool IsGPUFenceBehaviorBalanced();
|
||||||
bool IsGPUFenceBehaviorAccurate();
|
bool IsGPUFenceBehaviorAccurate();
|
||||||
|
bool IsGPUFenceBehaviorStrict();
|
||||||
|
|
||||||
bool IsFastmemEnabled();
|
bool IsFastmemEnabled();
|
||||||
void SetNceEnabled(bool is_64bit);
|
void SetNceEnabled(bool is_64bit);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
|||||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||||
ENUM(GpuAccuracy, Low, High);
|
ENUM(GpuAccuracy, Low, High);
|
||||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
||||||
ENUM(CpuBackend, Dynarmic, Nce);
|
ENUM(CpuBackend, Dynarmic, Nce);
|
||||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <algorithm>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#else
|
#else
|
||||||
@@ -21,36 +20,32 @@ namespace Common {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||||
static std::mutex vector_regions_mutex {};
|
|
||||||
|
|
||||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||||
|
|
||||||
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||||
// Not our problem
|
// Not our problem
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 addr = 0, addr2 = 0;
|
u64 addr = 0, addr2 = 0;
|
||||||
|
|
||||||
{
|
for (auto region: vector_regions) {
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
auto addr_shifted = exception_addr >> HostPageBits;
|
||||||
for (auto region: vector_regions) {
|
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||||
auto addr_shifted = exception_addr >> HostPageBits;
|
addr = addr_shifted;
|
||||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
}
|
||||||
addr = addr_shifted;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page-boundary accesses
|
// Page-boundary accesses
|
||||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||||
addr2 = addr_;
|
addr2 = addr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr != 0 || addr2 != 0) {
|
if (addr != 0 || addr2 != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,16 +77,8 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||||
} else if (info.State == MEM_COMMIT) {
|
|
||||||
DWORD old_protect {};
|
|
||||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
|
||||||
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
|
||||||
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (info.State != MEM_RESERVE) {
|
} else if (info.State != MEM_RESERVE) {
|
||||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,21 +96,6 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
#ifndef MAP_NOCORE
|
#ifndef MAP_NOCORE
|
||||||
#define MAP_NOCORE 0
|
#define MAP_NOCORE 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef MADV_FREE
|
|
||||||
#define MADV_FREE MADV_DONTNEED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void DecommitVectorPage(uintptr_t base) noexcept {
|
|
||||||
#if defined(_WIN32)
|
|
||||||
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
// Linux's MADV_DONTNEED zeros out pages for us
|
|
||||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
|
||||||
#else
|
|
||||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
if (auto page = HostPageSize; size % page != 0) {
|
if (auto page = HostPageSize; size % page != 0) {
|
||||||
@@ -136,8 +108,7 @@ void* AllocateMemoryPages(std::size_t size) noexcept {
|
|||||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
|
||||||
if (base != nullptr) {
|
if (base != nullptr) {
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||||
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
|
||||||
|
|
||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||||
@@ -163,8 +134,6 @@ void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
|||||||
if (!base)
|
if (!base)
|
||||||
return;
|
return;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
|
||||||
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
|
||||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||||
#else
|
#else
|
||||||
ASSERT(munmap(base, size) == 0);
|
ASSERT(munmap(base, size) == 0);
|
||||||
|
|||||||
@@ -28,14 +28,13 @@ constexpr u64 HostPageBits = 12;
|
|||||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||||
#else
|
#else
|
||||||
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||||
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||||
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
void DecommitVectorPage(uintptr_t base) noexcept;
|
|
||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -81,8 +80,8 @@ public:
|
|||||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
if (!IsCommittedPage(index)) {
|
||||||
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
CommitPage(index);
|
||||||
}
|
}
|
||||||
return base_ptr[index];
|
return base_ptr[index];
|
||||||
}
|
}
|
||||||
@@ -103,8 +102,9 @@ public:
|
|||||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (IsCommittedPage(index) || CommitPage(index))
|
if (!IsCommittedPage(index))
|
||||||
base_ptr[index] = value;
|
CommitPage(index);
|
||||||
|
base_ptr[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||||
@@ -114,7 +114,7 @@ public:
|
|||||||
const u64 end_page = AlignUp(base, HostPageSize);
|
const u64 end_page = AlignUp(base, HostPageSize);
|
||||||
const u64 first_size = (std::min)(end_page, end) - base;
|
const u64 first_size = (std::min)(end_page, end) - base;
|
||||||
|
|
||||||
if (IsCommittedPage(start)) {
|
if (IsCommittedPage(start / sizeof(T))) {
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,16 +124,11 @@ public:
|
|||||||
base = end_page;
|
base = end_page;
|
||||||
|
|
||||||
for (u64 page = base; page < end; page += HostPageSize) {
|
for (u64 page = base; page < end; page += HostPageSize) {
|
||||||
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||||
if (!IsCommittedPage(index)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end - page >= HostPageSize) {
|
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||||
DecommitPage(index);
|
|
||||||
} else {
|
|
||||||
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,30 +171,16 @@ private:
|
|||||||
return (val >> (page & 63)) & 1;
|
return (val >> (page & 63)) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool CommitPage(std::size_t index) noexcept {
|
constexpr void CommitPage(std::size_t index) noexcept {
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
if (!CommitVectorPage(page, true)) {
|
CommitVectorPage(page, true);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||||
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void DecommitPage(std::size_t index) noexcept {
|
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
|
||||||
|
|
||||||
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
|
||||||
DecommitVectorPage(page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t alloc_size{};
|
std::size_t alloc_size{};
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "common/zbic_compression.h"
|
|
||||||
|
|
||||||
#define ZSTD_ZBIC_SUPPORT 1
|
|
||||||
#define ZSTDLIB_VISIBLE static
|
|
||||||
#define ZSTDLIB_HIDDEN static
|
|
||||||
#define ZSTDERRORLIB_VISIBLE static
|
|
||||||
#define ZSTDERRORLIB_HIDDEN static
|
|
||||||
#undef ZSTD_MULTITHREAD
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
#undef _GNU_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "zstd.h"
|
|
||||||
#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol
|
|
||||||
#include "zstd.c"
|
|
||||||
#undef g_ZSTD_threading_useless_symbol
|
|
||||||
|
|
||||||
namespace Common::Compression {
|
|
||||||
|
|
||||||
bool IsZBIC(std::span<const u8> src) {
|
|
||||||
if (src.size() < sizeof(u32)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
u32 magic = 0;
|
|
||||||
std::memcpy(&magic, src.data(), sizeof(u32));
|
|
||||||
return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC")
|
|
||||||
}
|
|
||||||
|
|
||||||
int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src) {
|
|
||||||
if (dst.empty() || src.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size());
|
|
||||||
if (ZSTD_isError(res)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<int>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common::Compression
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Common::Compression {
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsZBIC(std::span<const u8> src);
|
|
||||||
|
|
||||||
[[nodiscard]] int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src);
|
|
||||||
|
|
||||||
} // namespace Common::Compression
|
|
||||||
@@ -23,8 +23,6 @@ add_library(core STATIC
|
|||||||
core_timing.h
|
core_timing.h
|
||||||
cpu_manager.cpp
|
cpu_manager.cpp
|
||||||
cpu_manager.h
|
cpu_manager.h
|
||||||
launch_params.cpp
|
|
||||||
launch_params.h
|
|
||||||
crypto/aes_util.cpp
|
crypto/aes_util.cpp
|
||||||
crypto/aes_util.h
|
crypto/aes_util.h
|
||||||
crypto/ctr_encryption_layer.cpp
|
crypto/ctr_encryption_layer.cpp
|
||||||
@@ -1205,9 +1203,6 @@ endif()
|
|||||||
|
|
||||||
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||||
if (MSVC)
|
|
||||||
target_link_libraries(core PRIVATE getopt)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||||
|
|||||||
+23
-45
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
|||||||
|
|
||||||
struct System::Impl {
|
struct System::Impl {
|
||||||
explicit Impl(System& system)
|
explicit Impl(System& system)
|
||||||
: kernel{system}
|
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
|
||||||
, fs_controller{system}
|
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
|
||||||
, hid_core{kernel}
|
|
||||||
, cpu_manager{system}
|
|
||||||
, reporter{system}
|
|
||||||
, profile_manager{}
|
|
||||||
{}
|
|
||||||
|
|
||||||
u64 program_id;
|
u64 program_id;
|
||||||
|
|
||||||
@@ -129,12 +124,6 @@ struct System::Impl {
|
|||||||
core_timing.SetMulticore(is_multicore);
|
core_timing.SetMulticore(is_multicore);
|
||||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||||
|
|
||||||
applet_manager.emplace(system);
|
|
||||||
frontend_applets.emplace(system);
|
|
||||||
apm_controller.emplace(core_timing);
|
|
||||||
arp_manager.emplace();
|
|
||||||
profile_manager.emplace();
|
|
||||||
|
|
||||||
// Create a default fs if one doesn't already exist.
|
// Create a default fs if one doesn't already exist.
|
||||||
if (virtual_filesystem == nullptr) {
|
if (virtual_filesystem == nullptr) {
|
||||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
@@ -144,7 +133,7 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create default implementations of applets if one is not provided.
|
// Create default implementations of applets if one is not provided.
|
||||||
frontend_applets->SetDefaultAppletsIfMissing();
|
frontend_applets.SetDefaultAppletsIfMissing();
|
||||||
|
|
||||||
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||||
|
|
||||||
@@ -387,7 +376,7 @@ struct System::Impl {
|
|||||||
|
|
||||||
// Register with applet manager
|
// Register with applet manager
|
||||||
// All threads are started, begin main process execution, now that we're in the clear
|
// All threads are started, begin main process execution, now that we're in the clear
|
||||||
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||||
|
|
||||||
if (Settings::values.gamecard_inserted) {
|
if (Settings::values.gamecard_inserted) {
|
||||||
if (Settings::values.gamecard_current_game) {
|
if (Settings::values.gamecard_current_game) {
|
||||||
@@ -439,27 +428,13 @@ struct System::Impl {
|
|||||||
core_timing.SyncPause(false);
|
core_timing.SyncPause(false);
|
||||||
Network::CancelPendingSocketOperations();
|
Network::CancelPendingSocketOperations();
|
||||||
kernel.SuspendEmulation(true);
|
kernel.SuspendEmulation(true);
|
||||||
kernel.ShutdownCores();
|
|
||||||
|
|
||||||
// Notify services helpers of shutdown
|
|
||||||
audio_core->Shutdown();
|
|
||||||
|
|
||||||
// Wait for threads/services to join
|
|
||||||
kernel.CloseServices();
|
kernel.CloseServices();
|
||||||
|
kernel.ShutdownCores();
|
||||||
// service shutdown
|
|
||||||
services.reset();
|
services.reset();
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
frontend_applets.reset();
|
|
||||||
applet_manager.reset();
|
|
||||||
apm_controller.reset();
|
|
||||||
arp_manager.reset();
|
|
||||||
profile_manager.reset();
|
|
||||||
|
|
||||||
fs_controller.Reset();
|
fs_controller.Reset();
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
core_timing.ClearPendingEvents();
|
core_timing.ClearPendingEvents();
|
||||||
core_timing.Reset();
|
|
||||||
app_loader.reset();
|
app_loader.reset();
|
||||||
audio_core.reset();
|
audio_core.reset();
|
||||||
gpu_core.reset();
|
gpu_core.reset();
|
||||||
@@ -476,6 +451,9 @@ struct System::Impl {
|
|||||||
room_member->SendGameInfo(game_info);
|
room_member->SendGameInfo(game_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset all glue registrations
|
||||||
|
arp_manager.ResetAll();
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Shutdown OK");
|
LOG_DEBUG(Core, "Shutdown OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,13 +482,13 @@ struct System::Impl {
|
|||||||
CpuManager cpu_manager;
|
CpuManager cpu_manager;
|
||||||
Reporter reporter;
|
Reporter reporter;
|
||||||
/// Applets
|
/// Applets
|
||||||
std::optional<Service::AM::AppletManager> applet_manager;
|
Service::AM::AppletManager applet_manager;
|
||||||
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets;
|
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
|
||||||
/// APM (Performance) services
|
/// APM (Performance) services
|
||||||
std::optional<Service::APM::Controller> apm_controller;
|
Service::APM::Controller apm_controller{core_timing};
|
||||||
/// Service State
|
/// Service State
|
||||||
std::optional<Service::Glue::ARPManager> arp_manager;
|
Service::Glue::ARPManager arp_manager;
|
||||||
std::optional<Service::Account::ProfileManager> profile_manager;
|
Service::Account::ProfileManager profile_manager;
|
||||||
/// Network instance
|
/// Network instance
|
||||||
Network::NetworkInstance network_instance;
|
Network::NetworkInstance network_instance;
|
||||||
Core::SpeedLimiter speed_limiter;
|
Core::SpeedLimiter speed_limiter;
|
||||||
@@ -837,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
||||||
impl->frontend_applets->SetFrontendAppletSet(std::move(set));
|
impl->frontend_applets.SetFrontendAppletSet(std::move(set));
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
|
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
|
||||||
return *impl->frontend_applets;
|
return impl->frontend_applets;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
|
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
|
||||||
return *impl->frontend_applets;
|
return impl->frontend_applets;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::AM::AppletManager& System::GetAppletManager() {
|
Service::AM::AppletManager& System::GetAppletManager() {
|
||||||
return *impl->applet_manager;
|
return impl->applet_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
||||||
@@ -890,27 +868,27 @@ const Reporter& System::GetReporter() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Service::Glue::ARPManager& System::GetARPManager() {
|
Service::Glue::ARPManager& System::GetARPManager() {
|
||||||
return *impl->arp_manager;
|
return impl->arp_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Glue::ARPManager& System::GetARPManager() const {
|
const Service::Glue::ARPManager& System::GetARPManager() const {
|
||||||
return *impl->arp_manager;
|
return impl->arp_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::APM::Controller& System::GetAPMController() {
|
Service::APM::Controller& System::GetAPMController() {
|
||||||
return *impl->apm_controller;
|
return impl->apm_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::APM::Controller& System::GetAPMController() const {
|
const Service::APM::Controller& System::GetAPMController() const {
|
||||||
return *impl->apm_controller;
|
return impl->apm_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::Account::ProfileManager& System::GetProfileManager() {
|
Service::Account::ProfileManager& System::GetProfileManager() {
|
||||||
return *impl->profile_manager;
|
return impl->profile_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||||
return *impl->profile_manager;
|
return impl->profile_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetExitLocked(bool locked) {
|
void System::SetExitLocked(bool locked) {
|
||||||
|
|||||||
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
|
|||||||
|
|
||||||
void CoreTiming::Reset() {
|
void CoreTiming::Reset() {
|
||||||
paused = true;
|
paused = true;
|
||||||
|
pause_event.Set();
|
||||||
|
event.Set();
|
||||||
if (timer_thread.joinable()) {
|
if (timer_thread.joinable()) {
|
||||||
timer_thread.request_stop();
|
timer_thread.request_stop();
|
||||||
pause_event.Set();
|
|
||||||
event.Set();
|
|
||||||
timer_thread.join();
|
timer_thread.join();
|
||||||
}
|
}
|
||||||
has_started = false;
|
has_started = false;
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
|
|||||||
auto reference = std::make_unique<FileReference>();
|
auto reference = std::make_unique<FileReference>();
|
||||||
this->InsertReferenceIntoListLocked(*reference);
|
this->InsertReferenceIntoListLocked(*reference);
|
||||||
|
|
||||||
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
|
auto file = std::shared_ptr<RealVfsFile>(
|
||||||
|
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
|
||||||
cache[path] = file;
|
cache[path] = file;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
@@ -176,7 +177,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
|||||||
|
|
||||||
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
@@ -184,7 +185,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
|
|||||||
if (!FS::CreateDirs(path)) {
|
if (!FS::CreateDirs(path)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||||
|
|||||||
@@ -82,9 +82,6 @@ class RealVfsFile : public VfsFile {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
|
||||||
const std::string& path, OpenMode perms = OpenMode::Read,
|
|
||||||
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
|
||||||
~RealVfsFile() override;
|
~RealVfsFile() override;
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
@@ -98,6 +95,9 @@ public:
|
|||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||||
|
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||||
|
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
||||||
|
|
||||||
RealVfsFilesystem& base;
|
RealVfsFilesystem& base;
|
||||||
std::unique_ptr<FileReference> reference;
|
std::unique_ptr<FileReference> reference;
|
||||||
@@ -113,8 +113,6 @@ class RealVfsDirectory : public VfsDirectory {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
|
||||||
OpenMode perms = OpenMode::Read);
|
|
||||||
~RealVfsDirectory() override;
|
~RealVfsDirectory() override;
|
||||||
|
|
||||||
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
||||||
@@ -140,6 +138,9 @@ public:
|
|||||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||||
|
OpenMode perms = OpenMode::Read);
|
||||||
|
|
||||||
template <typename T, typename R>
|
template <typename T, typename R>
|
||||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
|
|||||||
|
|
||||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
||||||
ASSERT(this->IsLocked());
|
ASSERT(this->IsLocked());
|
||||||
for (auto* thread : m_woken_dummy_threads)
|
if (m_woken_dummy_threads.size() > 0) {
|
||||||
thread->DummyThreadEndWait(kernel);
|
for (auto* thread : m_woken_dummy_threads)
|
||||||
m_woken_dummy_threads.clear();
|
thread->DummyThreadEndWait(kernel);
|
||||||
|
m_woken_dummy_threads.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
|
|||||||
KThread* cur_child = nullptr;
|
KThread* cur_child = nullptr;
|
||||||
{
|
{
|
||||||
KScopedLightLock proc_lk(process->GetListLock());
|
KScopedLightLock proc_lk(process->GetListLock());
|
||||||
|
|
||||||
auto& thread_list = process->GetThreadList();
|
auto& thread_list = process->GetThreadList();
|
||||||
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
|
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
|
||||||
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
|
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
|
||||||
|
|||||||
@@ -1236,13 +1236,18 @@ namespace Kernel {
|
|||||||
KScopedSchedulerLock sl{kernel};
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
// Determine if this is the first termination request.
|
// Determine if this is the first termination request.
|
||||||
// Perform an atomic compare-and-swap from false to true.
|
const bool first_request = [&]() -> bool {
|
||||||
bool expected = false;
|
// Perform an atomic compare-and-swap from false to true.
|
||||||
|
bool expected = false;
|
||||||
|
return m_termination_requested.compare_exchange_strong(expected, true);
|
||||||
|
}();
|
||||||
|
|
||||||
// If this is the first request, start termination procedure.
|
// If this is the first request, start termination procedure.
|
||||||
if (m_termination_requested.compare_exchange_strong(expected, true)) {
|
if (first_request) {
|
||||||
// If the thread is in initialized state, just change state to terminated.
|
// If the thread is in initialized state, just change state to terminated.
|
||||||
if (this->GetState() == ThreadState::Initialized) {
|
if (this->GetState() == ThreadState::Initialized) {
|
||||||
return m_thread_state = ThreadState::Terminated;
|
m_thread_state = ThreadState::Terminated;
|
||||||
|
return ThreadState::Terminated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the terminating dpc.
|
// Register the terminating dpc.
|
||||||
@@ -1276,6 +1281,7 @@ namespace Kernel {
|
|||||||
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
|
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->GetState();
|
return this->GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/thread.h"
|
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/k_worker_task.h"
|
#include "core/hle/kernel/k_worker_task.h"
|
||||||
@@ -28,33 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KWorkerTaskManager::KWorkerTaskManager(KernelCore& kernel) {
|
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
|
||||||
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("KWorkerManager");
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
KWorkerTask* t;
|
|
||||||
{
|
|
||||||
std::unique_lock lk{m_task_mutex};
|
|
||||||
m_task_cv.wait(lk, m_waiting_thread.get_stop_token(), [&]() {
|
|
||||||
return !m_task_queue.empty();
|
|
||||||
});
|
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
t = m_task_queue.back();
|
|
||||||
m_task_queue.pop_back();
|
|
||||||
}
|
|
||||||
t->DoWorkerTask(kernel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
KWorkerTaskManager::~KWorkerTaskManager() {
|
|
||||||
if (m_waiting_thread.joinable()) {
|
|
||||||
m_waiting_thread.request_stop();
|
|
||||||
m_task_cv.notify_one();
|
|
||||||
m_waiting_thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
||||||
ASSERT(type <= WorkerType::Count);
|
ASSERT(type <= WorkerType::Count);
|
||||||
@@ -63,11 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
|
|||||||
|
|
||||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
||||||
KScopedSchedulerLock sl(kernel);
|
KScopedSchedulerLock sl(kernel);
|
||||||
{
|
m_waiting_thread.QueueWork([&kernel, task]() {
|
||||||
std::scoped_lock lk{m_task_mutex};
|
// Do the task.
|
||||||
m_task_queue.emplace_back(task);
|
task->DoWorkerTask(kernel);
|
||||||
}
|
});
|
||||||
m_task_cv.notify_one();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <thread>
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
|
|
||||||
@@ -23,17 +18,15 @@ public:
|
|||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
KWorkerTaskManager(KernelCore& kernel);
|
KWorkerTaskManager();
|
||||||
~KWorkerTaskManager();
|
|
||||||
|
|
||||||
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
|
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
||||||
std::jthread m_waiting_thread;
|
|
||||||
|
|
||||||
std::mutex m_task_mutex;
|
private:
|
||||||
std::condition_variable_any m_task_cv;
|
Common::ThreadWorker m_waiting_thread;
|
||||||
std::vector<KWorkerTask*> m_task_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -80,10 +80,7 @@ struct KernelCore::Impl {
|
|||||||
// so it will be statically given a TLS slot anyways.
|
// so it will be statically given a TLS slot anyways.
|
||||||
static inline thread_local ThreadLocalData tls_data = {};
|
static inline thread_local ThreadLocalData tls_data = {};
|
||||||
|
|
||||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
|
||||||
: worker_task_manager{kernel_}
|
|
||||||
, system{system_}
|
|
||||||
{
|
|
||||||
tls_data.lock = true;
|
tls_data.lock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
|
|||||||
return *impl->hardware_timer;
|
return *impl->hardware_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
|
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
|
||||||
if (!impl->global_object_list_container)
|
return *impl->global_object_list_container;
|
||||||
return nullptr;
|
|
||||||
return std::addressof(*impl->global_object_list_container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
|
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
|
||||||
if (!impl->global_object_list_container)
|
return *impl->global_object_list_container;
|
||||||
return nullptr;
|
|
||||||
return std::addressof(*impl->global_object_list_container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::PrepareReschedule(std::size_t id) {
|
void KernelCore::PrepareReschedule(std::size_t id) {
|
||||||
@@ -1008,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
|||||||
|
|
||||||
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
||||||
auto* manager = server_manager.get();
|
auto* manager = server_manager.get();
|
||||||
if (!impl->is_shutting_down) {
|
|
||||||
|
{
|
||||||
std::scoped_lock lk{impl->server_lock};
|
std::scoped_lock lk{impl->server_lock};
|
||||||
|
if (impl->is_shutting_down) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
impl->server_managers.emplace_back(std::move(server_manager));
|
impl->server_managers.emplace_back(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
manager->LoopProcess();
|
manager->LoopProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::ShutdownCores() {
|
void KernelCore::ShutdownCores() {
|
||||||
// Notify shutdown (pre-emptively)
|
|
||||||
for (auto& sm : impl->server_managers) {
|
|
||||||
sm->NotifyShutdown();
|
|
||||||
}
|
|
||||||
impl->TerminateAllProcesses();
|
impl->TerminateAllProcesses();
|
||||||
KScopedSchedulerLock lk{*this};
|
KScopedSchedulerLock lk{*this};
|
||||||
for (auto* thread : impl->shutdown_threads)
|
for (auto* thread : impl->shutdown_threads)
|
||||||
|
|||||||
@@ -175,8 +175,9 @@ public:
|
|||||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||||
void PrepareReschedule(std::size_t id);
|
void PrepareReschedule(std::size_t id);
|
||||||
|
|
||||||
KAutoObjectWithListContainer* ObjectListContainer();
|
KAutoObjectWithListContainer& ObjectListContainer();
|
||||||
const KAutoObjectWithListContainer* ObjectListContainer() const;
|
|
||||||
|
const KAutoObjectWithListContainer& ObjectListContainer() const;
|
||||||
|
|
||||||
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
||||||
/// leaks after emulation has been shutdown.
|
/// leaks after emulation has been shutdown.
|
||||||
|
|||||||
@@ -151,8 +151,7 @@ public:
|
|||||||
const bool is_initialized = this->IsInitialized();
|
const bool is_initialized = this->IsInitialized();
|
||||||
uintptr_t arg = 0;
|
uintptr_t arg = 0;
|
||||||
if (is_initialized) {
|
if (is_initialized) {
|
||||||
if (auto const olc = kernel.ObjectListContainer(); olc)
|
kernel.ObjectListContainer().Unregister(this);
|
||||||
olc->Unregister(this);
|
|
||||||
arg = this->GetPostDestroyArgument();
|
arg = this->GetPostDestroyArgument();
|
||||||
this->Finalize(kernel);
|
this->Finalize(kernel);
|
||||||
}
|
}
|
||||||
@@ -176,7 +175,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
|
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
|
||||||
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
|
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
|
||||||
kernel.ObjectListContainer()->Initialize();
|
kernel.ObjectListContainer().Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Derived* Create(KernelCore& kernel) {
|
static Derived* Create(KernelCore& kernel) {
|
||||||
@@ -187,7 +186,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void Register(KernelCore& kernel, Derived* obj) {
|
static void Register(KernelCore& kernel, Derived* obj) {
|
||||||
return kernel.ObjectListContainer()->Register(obj);
|
return kernel.ObjectListContainer().Register(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t GetObjectSize(KernelCore& kernel) {
|
static size_t GetObjectSize(KernelCore& kernel) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -18,9 +15,13 @@ void LoopProcess(Core::System& system) {
|
|||||||
WindowSystem window_system(system);
|
WindowSystem window_system(system);
|
||||||
ButtonPoller button_poller(system, window_system);
|
ButtonPoller button_poller(system, window_system);
|
||||||
EventObserver event_observer(system, window_system);
|
EventObserver event_observer(system, window_system);
|
||||||
|
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
server_manager->RegisterNamedService("appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
|
|
||||||
server_manager->RegisterNamedService("appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
|
server_manager->RegisterNamedService(
|
||||||
|
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
|
||||||
|
server_manager->RegisterNamedService(
|
||||||
|
"appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,25 +26,24 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
|||||||
m_window_system.SetEventObserver(this);
|
m_window_system.SetEventObserver(this);
|
||||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||||
system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() {
|
m_thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
auto const stop_token = m_stop_source.get_token();
|
Common::SetCurrentThreadName("am:EventObserver");
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto* signaled_holder = this->WaitSignaled(stop_token);
|
auto* signaled_holder = this->WaitSignaled(stop_token);
|
||||||
if (stop_token.stop_requested() || !signaled_holder)
|
if (!signaled_holder)
|
||||||
break;
|
break;
|
||||||
this->Process(signaled_holder);
|
this->Process(signaled_holder);
|
||||||
}
|
}
|
||||||
m_process_cv.notify_one();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EventObserver::~EventObserver() {
|
EventObserver::~EventObserver() {
|
||||||
// Signal thread and wait for processing to finish.
|
// Signal thread and wait for processing to finish.
|
||||||
m_stop_source.request_stop();
|
if (m_thread.joinable()) {
|
||||||
m_wakeup_event.Signal(m_system.Kernel());
|
// Signal thread and wait for processing to finish.
|
||||||
{
|
m_thread.request_stop();
|
||||||
std::unique_lock lk{m_process_mutex};
|
m_wakeup_event.Signal(m_system.Kernel());
|
||||||
m_process_cv.wait(lk);
|
m_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free remaining owned sessions.
|
// Free remaining owned sessions.
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <stop_token>
|
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/hle/service/kernel_helpers.h"
|
#include "core/hle/service/kernel_helpers.h"
|
||||||
@@ -64,9 +62,7 @@ private:
|
|||||||
MultiWait m_deferred_wait_list;
|
MultiWait m_deferred_wait_list;
|
||||||
|
|
||||||
// Processing thread.
|
// Processing thread.
|
||||||
std::stop_source m_stop_source{};
|
std::jthread m_thread{};
|
||||||
std::mutex m_process_mutex;
|
|
||||||
std::condition_variable m_process_cv{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||||
s32 z_index = Background;
|
s32 z_index = Background;
|
||||||
if (is_overlay) {
|
if (is_overlay) {
|
||||||
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
|
z_index = Overlay;
|
||||||
} else if (inherited_foreground) {
|
} else if (inherited_foreground) {
|
||||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
TimeManager::TimeManager(Core::System& system)
|
TimeManager::TimeManager(Core::System& system)
|
||||||
: m_steady_clock_resource{system}, m_time_zone_binary{system}
|
: m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
|
||||||
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker}
|
system,
|
||||||
{
|
m_steady_clock_resource,
|
||||||
|
m_file_timestamp_worker} {
|
||||||
m_time_m =
|
m_time_m =
|
||||||
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
|
||||||
|
|
||||||
|
|||||||
@@ -20,17 +20,15 @@
|
|||||||
|
|
||||||
namespace Service::Glue::Time {
|
namespace Service::Glue::Time {
|
||||||
|
|
||||||
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker)
|
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
|
||||||
: m_system{system}
|
FileTimestampWorker& file_timestamp_worker)
|
||||||
, m_ctx{m_system, "Glue:TimeWorker"}
|
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
|
||||||
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")}
|
"Glue:TimeWorker:Event")},
|
||||||
, m_steady_clock_resource{steady_clock_resource}
|
m_steady_clock_resource{steady_clock_resource},
|
||||||
, m_file_timestamp_worker{file_timestamp_worker}
|
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
|
||||||
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")}
|
"Glue:TimeWorker:SteadyClockTimerEvent")},
|
||||||
, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}
|
m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
|
||||||
, m_alarm_worker{m_system, m_steady_clock_resource}
|
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
|
||||||
, m_pm_state_change_handler{m_alarm_worker}
|
|
||||||
{
|
|
||||||
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
|
||||||
"Time::SteadyClockEvent",
|
"Time::SteadyClockEvent",
|
||||||
[this](s64 time,
|
[this](s64 time,
|
||||||
@@ -49,16 +47,14 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
|
|||||||
}
|
}
|
||||||
|
|
||||||
TimeWorker::~TimeWorker() {
|
TimeWorker::~TimeWorker() {
|
||||||
// Wait for processing to stop
|
|
||||||
m_stop_source.request_stop();
|
|
||||||
m_local_clock_event->Signal(m_system.Kernel());
|
m_local_clock_event->Signal(m_system.Kernel());
|
||||||
m_network_clock_event->Signal(m_system.Kernel());
|
m_network_clock_event->Signal(m_system.Kernel());
|
||||||
m_ephemeral_clock_event->Signal(m_system.Kernel());
|
m_ephemeral_clock_event->Signal(m_system.Kernel());
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
|
|
||||||
|
m_thread.request_stop();
|
||||||
m_event->Signal(m_system.Kernel());
|
m_event->Signal(m_system.Kernel());
|
||||||
{
|
m_thread.join();
|
||||||
std::unique_lock lk{m_process_mutex};
|
|
||||||
m_process_cv.wait(lk);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ctx.CloseEvent(m_event);
|
m_ctx.CloseEvent(m_event);
|
||||||
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
|
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
|
||||||
@@ -126,162 +122,166 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TimeWorker::StartThread() {
|
void TimeWorker::StartThread() {
|
||||||
m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() {
|
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
|
||||||
auto const stop_token = m_stop_source.get_token();
|
}
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
enum class EventType : s32 {
|
|
||||||
Exit = 0,
|
|
||||||
PowerStateChange = 1,
|
|
||||||
SignalAlarms = 2,
|
|
||||||
UpdateLocalSystemClock = 3,
|
|
||||||
UpdateNetworkSystemClock = 4,
|
|
||||||
UpdateEphemeralSystemClock = 5,
|
|
||||||
UpdateSteadyClock = 6,
|
|
||||||
UpdateFileTimestamp = 7,
|
|
||||||
AutoCorrect = 8,
|
|
||||||
};
|
|
||||||
s32 index{};
|
|
||||||
if (m_pm_state_change_handler.m_priority != 0) {
|
|
||||||
// TODO: gIPmModuleService::GetEvent() 1
|
|
||||||
index = WaitAny(
|
|
||||||
m_system.Kernel(),
|
|
||||||
&m_event->GetReadableEvent(), // 0
|
|
||||||
&m_alarm_worker.GetEvent() // 1
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// TODO: gIPmModuleService::GetEvent() 1
|
|
||||||
index = WaitAny(
|
|
||||||
m_system.Kernel(),
|
|
||||||
&m_event->GetReadableEvent(), // 0
|
|
||||||
&m_alarm_worker.GetEvent(), // 1
|
|
||||||
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
|
|
||||||
m_local_clock_event, // 3
|
|
||||||
m_network_clock_event, // 4
|
|
||||||
m_ephemeral_clock_event, // 5
|
|
||||||
&m_timer_steady_clock->GetReadableEvent(), // 6
|
|
||||||
&m_timer_file_system->GetReadableEvent(), // 7
|
|
||||||
m_standard_user_auto_correct_clock_event // 8
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop_token.stop_requested())
|
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
|
||||||
break;
|
Common::SetCurrentThreadName("TimeWorker");
|
||||||
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
|
||||||
|
|
||||||
switch (EventType(index)) {
|
while (!stop_token.stop_requested()) {
|
||||||
case EventType::Exit:
|
enum class EventType : s32 {
|
||||||
return;
|
Exit = 0,
|
||||||
|
PowerStateChange = 1,
|
||||||
|
SignalAlarms = 2,
|
||||||
|
UpdateLocalSystemClock = 3,
|
||||||
|
UpdateNetworkSystemClock = 4,
|
||||||
|
UpdateEphemeralSystemClock = 5,
|
||||||
|
UpdateSteadyClock = 6,
|
||||||
|
UpdateFileTimestamp = 7,
|
||||||
|
AutoCorrect = 8,
|
||||||
|
};
|
||||||
|
|
||||||
case EventType::PowerStateChange:
|
s32 index{};
|
||||||
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
|
|
||||||
if (m_pm_state_change_handler.m_priority <= 1) {
|
|
||||||
m_alarm_worker.OnPowerStateChanged();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EventType::SignalAlarms:
|
if (m_pm_state_change_handler.m_priority != 0) {
|
||||||
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
|
// TODO: gIPmModuleService::GetEvent() 1
|
||||||
m_time_m->CheckAndSignalAlarms();
|
index = WaitAny(m_system.Kernel(),
|
||||||
break;
|
&m_event->GetReadableEvent(), // 0
|
||||||
|
&m_alarm_worker.GetEvent() // 1
|
||||||
case EventType::UpdateLocalSystemClock: {
|
);
|
||||||
m_local_clock_event->Clear(m_system.Kernel());
|
} else {
|
||||||
|
// TODO: gIPmModuleService::GetEvent() 1
|
||||||
Service::PSC::Time::SystemClockContext context{};
|
index = WaitAny(m_system.Kernel(),
|
||||||
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
|
&m_event->GetReadableEvent(), // 0
|
||||||
|
&m_alarm_worker.GetEvent(), // 1
|
||||||
m_set_sys->SetUserSystemClockContext(context);
|
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
|
||||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
m_local_clock_event, // 3
|
||||||
break;
|
m_network_clock_event, // 4
|
||||||
}
|
m_ephemeral_clock_event, // 5
|
||||||
|
&m_timer_steady_clock->GetReadableEvent(), // 6
|
||||||
case EventType::UpdateNetworkSystemClock: {
|
&m_timer_file_system->GetReadableEvent(), // 7
|
||||||
m_network_clock_event->Clear(m_system.Kernel());
|
m_standard_user_auto_correct_clock_event // 8
|
||||||
|
);
|
||||||
Service::PSC::Time::SystemClockContext context{};
|
|
||||||
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
|
|
||||||
|
|
||||||
m_set_sys->SetNetworkSystemClockContext(context);
|
|
||||||
|
|
||||||
s64 time{};
|
|
||||||
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]] auto offset_before{
|
|
||||||
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
|
|
||||||
// TODO system report "standard_netclock_operation"
|
|
||||||
// "clock_time" = time
|
|
||||||
// "context_offset_before" = offset_before
|
|
||||||
// "context_offset_after" = context.offset
|
|
||||||
m_report_network_clock_context = context;
|
|
||||||
if (!m_ig_report_network_clock_context_set) {
|
|
||||||
m_ig_report_network_clock_context_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case EventType::UpdateEphemeralSystemClock: {
|
|
||||||
m_ephemeral_clock_event->Clear(m_system.Kernel());
|
|
||||||
|
|
||||||
Service::PSC::Time::SystemClockContext context{};
|
|
||||||
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
|
|
||||||
if (res != ResultSuccess) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
s64 time{};
|
|
||||||
res = m_ephemeral_clock->GetCurrentTime(&time);
|
|
||||||
if (res != ResultSuccess) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
|
|
||||||
? m_report_ephemeral_clock_context.offset
|
|
||||||
: 0};
|
|
||||||
// TODO system report "ephemeral_netclock_operation"
|
|
||||||
// "clock_time" = time
|
|
||||||
// "context_offset_before" = offset_before
|
|
||||||
// "context_offset_after" = context.offset
|
|
||||||
m_report_ephemeral_clock_context = context;
|
|
||||||
if (!m_ig_report_ephemeral_clock_context_set) {
|
|
||||||
m_ig_report_ephemeral_clock_context_set = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case EventType::UpdateSteadyClock:
|
|
||||||
m_timer_steady_clock->Clear(m_system.Kernel());
|
|
||||||
|
|
||||||
m_steady_clock_resource.UpdateTime();
|
|
||||||
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EventType::UpdateFileTimestamp:
|
|
||||||
m_timer_file_system->Clear(m_system.Kernel());
|
|
||||||
m_file_timestamp_worker.SetFilesystemPosixTime();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EventType::AutoCorrect: {
|
|
||||||
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
|
|
||||||
|
|
||||||
bool automatic_correction{};
|
|
||||||
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction));
|
|
||||||
|
|
||||||
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
|
||||||
R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
|
|
||||||
|
|
||||||
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
|
|
||||||
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_process_cv.notify_one();
|
|
||||||
});
|
switch (static_cast<EventType>(index)) {
|
||||||
|
case EventType::Exit:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EventType::PowerStateChange:
|
||||||
|
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
|
||||||
|
if (m_pm_state_change_handler.m_priority <= 1) {
|
||||||
|
m_alarm_worker.OnPowerStateChanged();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EventType::SignalAlarms:
|
||||||
|
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
|
||||||
|
m_time_m->CheckAndSignalAlarms();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EventType::UpdateLocalSystemClock: {
|
||||||
|
m_local_clock_event->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
Service::PSC::Time::SystemClockContext context{};
|
||||||
|
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
|
||||||
|
|
||||||
|
m_set_sys->SetUserSystemClockContext(context);
|
||||||
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventType::UpdateNetworkSystemClock: {
|
||||||
|
m_network_clock_event->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
Service::PSC::Time::SystemClockContext context{};
|
||||||
|
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
|
||||||
|
|
||||||
|
m_set_sys->SetNetworkSystemClockContext(context);
|
||||||
|
|
||||||
|
s64 time{};
|
||||||
|
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] auto offset_before{
|
||||||
|
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
|
||||||
|
// TODO system report "standard_netclock_operation"
|
||||||
|
// "clock_time" = time
|
||||||
|
// "context_offset_before" = offset_before
|
||||||
|
// "context_offset_after" = context.offset
|
||||||
|
m_report_network_clock_context = context;
|
||||||
|
if (!m_ig_report_network_clock_context_set) {
|
||||||
|
m_ig_report_network_clock_context_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventType::UpdateEphemeralSystemClock: {
|
||||||
|
m_ephemeral_clock_event->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
Service::PSC::Time::SystemClockContext context{};
|
||||||
|
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
|
||||||
|
if (res != ResultSuccess) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 time{};
|
||||||
|
res = m_ephemeral_clock->GetCurrentTime(&time);
|
||||||
|
if (res != ResultSuccess) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
|
||||||
|
? m_report_ephemeral_clock_context.offset
|
||||||
|
: 0};
|
||||||
|
// TODO system report "ephemeral_netclock_operation"
|
||||||
|
// "clock_time" = time
|
||||||
|
// "context_offset_before" = offset_before
|
||||||
|
// "context_offset_after" = context.offset
|
||||||
|
m_report_ephemeral_clock_context = context;
|
||||||
|
if (!m_ig_report_ephemeral_clock_context_set) {
|
||||||
|
m_ig_report_ephemeral_clock_context_set = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EventType::UpdateSteadyClock:
|
||||||
|
m_timer_steady_clock->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
m_steady_clock_resource.UpdateTime();
|
||||||
|
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EventType::UpdateFileTimestamp:
|
||||||
|
m_timer_file_system->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
m_file_timestamp_worker.SetFilesystemPosixTime();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EventType::AutoCorrect: {
|
||||||
|
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
|
||||||
|
|
||||||
|
bool automatic_correction{};
|
||||||
|
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
|
||||||
|
&automatic_correction));
|
||||||
|
|
||||||
|
Service::PSC::Time::SteadyClockTimePoint time_point{};
|
||||||
|
R_ASSERT(
|
||||||
|
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
|
||||||
|
|
||||||
|
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
|
||||||
|
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Glue::Time
|
} // namespace Service::Glue::Time
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/k_event.h"
|
#include "core/hle/kernel/k_event.h"
|
||||||
#include "core/hle/service/glue/time/alarm_worker.h"
|
#include "core/hle/service/glue/time/alarm_worker.h"
|
||||||
@@ -37,17 +33,17 @@ public:
|
|||||||
|
|
||||||
void StartThread();
|
void StartThread();
|
||||||
|
|
||||||
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T GetSettingsItemValue(const std::string& category, const std::string& name);
|
T GetSettingsItemValue(const std::string& category, const std::string& name);
|
||||||
|
|
||||||
|
void ThreadFunc(std::stop_token stop_token);
|
||||||
|
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
KernelHelpers::ServiceContext m_ctx;
|
KernelHelpers::ServiceContext m_ctx;
|
||||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||||
|
|
||||||
std::mutex m_process_mutex;
|
std::jthread m_thread;
|
||||||
std::condition_variable m_process_cv;
|
|
||||||
std::stop_source m_stop_source;
|
|
||||||
|
|
||||||
Kernel::KEvent* m_event{};
|
Kernel::KEvent* m_event{};
|
||||||
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
|
||||||
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
|
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
|||||||
|
|
||||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||||
LOG_DEBUG(Service_PSC, "called");
|
LOG_DEBUG(Service_PSC, "called");
|
||||||
*out_receiver = std::make_shared<IReceiver>(system);
|
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,10 +98,8 @@ ServerManager::~ServerManager() {
|
|||||||
// Signal stop.
|
// Signal stop.
|
||||||
m_stop_source.request_stop();
|
m_stop_source.request_stop();
|
||||||
m_wakeup_event->Signal(m_system.Kernel());
|
m_wakeup_event->Signal(m_system.Kernel());
|
||||||
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
|
|
||||||
|
|
||||||
|
// Wait for processing to stop.
|
||||||
// Wait for processing to stop. Schedule
|
|
||||||
m_stopped.Wait();
|
m_stopped.Wait();
|
||||||
m_threads.clear();
|
m_threads.clear();
|
||||||
|
|
||||||
@@ -255,14 +253,6 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Notifies that the system is shutting down (pre-emptively terminate threads)
|
|
||||||
void ServerManager::NotifyShutdown() {
|
|
||||||
m_stop_source.request_stop();
|
|
||||||
// Wake them up regardless
|
|
||||||
m_wakeup_event->Signal(m_system.Kernel());
|
|
||||||
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ServerManager::LoopProcess() {
|
Result ServerManager::LoopProcess() {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
@@ -295,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
|
|||||||
this->LinkDeferred();
|
this->LinkDeferred();
|
||||||
|
|
||||||
// If we're done, return before we start waiting.
|
// If we're done, return before we start waiting.
|
||||||
if (m_stop_source.stop_requested())
|
if (m_stop_source.stop_requested()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
|
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
|
||||||
if (selected == std::addressof(*m_wakeup_holder)) {
|
if (selected == std::addressof(*m_wakeup_holder)) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -52,7 +49,6 @@ public:
|
|||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
void NotifyShutdown();
|
|
||||||
|
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
|||||||
{"friends", &Friend::LoopProcess},
|
{"friends", &Friend::LoopProcess},
|
||||||
{"settings", &Set::LoopProcess},
|
{"settings", &Set::LoopProcess},
|
||||||
{"psc", &PSC::LoopProcess},
|
{"psc", &PSC::LoopProcess},
|
||||||
|
{"glue", &Glue::LoopProcess},
|
||||||
{"grc", &GRC::LoopProcess},
|
{"grc", &GRC::LoopProcess},
|
||||||
{"hid", &HID::LoopProcess},
|
{"hid", &HID::LoopProcess},
|
||||||
{"jit", &JIT::LoopProcess},
|
{"jit", &JIT::LoopProcess},
|
||||||
@@ -152,7 +153,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
|||||||
{"usb", &USB::LoopProcess},
|
{"usb", &USB::LoopProcess},
|
||||||
{"i2c", &I2C::LoopProcess},
|
{"i2c", &I2C::LoopProcess},
|
||||||
{"gpio", &GPIO::LoopProcess},
|
{"gpio", &GPIO::LoopProcess},
|
||||||
{"glue", &Glue::LoopProcess},
|
|
||||||
})
|
})
|
||||||
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,14 +305,16 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
|||||||
SM::~SM() = default;
|
SM::~SM() = default;
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
|
auto& service_manager = system.ServiceManager();
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
Kernel::KEvent* deferral_event{};
|
Kernel::KEvent* deferral_event{};
|
||||||
server_manager->ManageDeferral(&deferral_event);
|
server_manager->ManageDeferral(&deferral_event);
|
||||||
system.ServiceManager().SetDeferralEvent(deferral_event);
|
service_manager.SetDeferralEvent(deferral_event);
|
||||||
|
|
||||||
auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
|
auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
|
||||||
server_manager->ManageNamedPort("sm:", [sm_service] {
|
server_manager->ManageNamedPort("sm:", [sm_service] { return sm_service; });
|
||||||
return sm_service;
|
|
||||||
});
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/service/vi/conductor.h"
|
#include "core/hle/service/vi/conductor.h"
|
||||||
#include "core/hle/service/vi/container.h"
|
#include "core/hle/service/vi/container.h"
|
||||||
#include "core/hle/service/vi/display_list.h"
|
#include "core/hle/service/vi/display_list.h"
|
||||||
@@ -30,29 +29,24 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (system.IsMulticore()) {
|
if (system.IsMulticore()) {
|
||||||
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_event = Core::Timing::CreateEvent(
|
||||||
m_signal.Set();
|
"ScreenComposition",
|
||||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
m_signal.Set();
|
||||||
|
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||||
|
});
|
||||||
|
|
||||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||||
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() {
|
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
|
||||||
auto const stop_token = m_thread.get_stop_token();
|
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
|
|
||||||
Common::SetCurrentThreadToPerformanceCores();
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
m_signal.Wait();
|
|
||||||
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->ProcessVsync();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
m_event = Core::Timing::CreateEvent(
|
||||||
this->ProcessVsync();
|
"ScreenComposition",
|
||||||
return std::chrono::nanoseconds(this->GetNextTicks());
|
[this](s64 time,
|
||||||
});
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
|
this->ProcessVsync();
|
||||||
|
return std::chrono::nanoseconds(this->GetNextTicks());
|
||||||
|
});
|
||||||
|
|
||||||
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
|
||||||
}
|
}
|
||||||
@@ -60,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
|
|||||||
|
|
||||||
Conductor::~Conductor() {
|
Conductor::~Conductor() {
|
||||||
m_system.CoreTiming().UnscheduleEvent(m_event);
|
m_system.CoreTiming().UnscheduleEvent(m_event);
|
||||||
if (m_thread.joinable()) {
|
|
||||||
|
if (m_system.IsMulticore()) {
|
||||||
m_thread.request_stop();
|
m_thread.request_stop();
|
||||||
m_signal.Set();
|
m_signal.Set();
|
||||||
m_thread.join();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +83,22 @@ void Conductor::ProcessVsync() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conductor::VsyncThread(std::stop_token token) {
|
||||||
|
Common::SetCurrentThreadName("VSyncThread");
|
||||||
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
|
||||||
|
Common::SetCurrentThreadToPerformanceCores();
|
||||||
|
|
||||||
|
while (!token.stop_requested()) {
|
||||||
|
m_signal.Wait();
|
||||||
|
|
||||||
|
if (m_system.IsShuttingDown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->ProcessVsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s64 Conductor::GetNextTicks() const {
|
s64 Conductor::GetNextTicks() const {
|
||||||
const auto& settings = Settings::values;
|
const auto& settings = Settings::values;
|
||||||
auto speed_scale = 1.f;
|
auto speed_scale = 1.f;
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/logging.h"
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "common/string_util.h"
|
|
||||||
#include "common/scm_rev.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
|
||||||
#include "core/launch_params.h"
|
|
||||||
|
|
||||||
#undef _UNICODE
|
|
||||||
#include <getopt.h>
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
|
|
||||||
LaunchParams p{};
|
|
||||||
|
|
||||||
int option_index = 0;
|
|
||||||
static struct option long_options[] = {
|
|
||||||
// clang-format off
|
|
||||||
{"debug", no_argument, 0, 'd'},
|
|
||||||
{"config", required_argument, 0, 'c'},
|
|
||||||
{"fullscreen", no_argument, 0, 'f'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"game", required_argument, 0, 'g'},
|
|
||||||
{"multiplayer", required_argument, 0, 'm'},
|
|
||||||
{"program", optional_argument, 0, 'p'},
|
|
||||||
{"user", required_argument, 0, 'u'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{"input-profile", no_argument, 0, 'i'},
|
|
||||||
{"null-render", no_argument, 0, 'n'},
|
|
||||||
{"singlecore", no_argument, 0, 's'},
|
|
||||||
{"filter", no_argument, 0, 'x'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
// clang-format on
|
|
||||||
};
|
|
||||||
|
|
||||||
while (optind < argc) {
|
|
||||||
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
|
|
||||||
switch (char(arg)) {
|
|
||||||
case 'd':
|
|
||||||
p.override_gdb_port = uint16_t(atoi(optarg));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
p.config_path = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
p.fullscreen = true;
|
|
||||||
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
p.print_help = true;
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
p.filepath = std::string(optarg);
|
|
||||||
break;
|
|
||||||
case 'i': {
|
|
||||||
p.input_profile = std::string(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm': {
|
|
||||||
p.use_multiplayer = true;
|
|
||||||
const std::string str_arg(optarg);
|
|
||||||
// regex to check if the format is nickname:password@ip:port
|
|
||||||
// with optional :password
|
|
||||||
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
|
||||||
if (std::regex_match(str_arg, re)) {
|
|
||||||
std::smatch match;
|
|
||||||
std::regex_search(str_arg, match, re);
|
|
||||||
ASSERT(match.size() == 5);
|
|
||||||
p.nickname = match[1];
|
|
||||||
p.password = match[2];
|
|
||||||
p.address = match[3];
|
|
||||||
if (!match[4].str().empty()) {
|
|
||||||
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
|
||||||
}
|
|
||||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
|
||||||
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
|
||||||
ASSERT(!p.address.empty() && "Address is empty");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'p':
|
|
||||||
p.program_args = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
case 'u': {
|
|
||||||
// Launch game with a specific user
|
|
||||||
bool argument_ok = isdigit(optarg[0]);
|
|
||||||
p.selected_user = atoi(optarg);
|
|
||||||
if (!argument_ok) {
|
|
||||||
// try to look it up by username, only finds the first username that matches.
|
|
||||||
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
|
|
||||||
if (user_idx != std::nullopt) {
|
|
||||||
p.selected_user = user_idx.value();
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
|
|
||||||
Settings::values.current_user = s32(*p.selected_user);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'v':
|
|
||||||
p.print_version = true;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
p.force_null_render = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
p.force_single_core = true;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
p.log_filter = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
|
|
||||||
if (strcmp(argv[optind], "-hlaunch") == 0) {
|
|
||||||
p.launch_hlaunch = true;
|
|
||||||
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
|
|
||||||
p.launch_qlaunch = true;
|
|
||||||
} else if (strcmp(argv[optind], "-setup") == 0) {
|
|
||||||
p.launch_setup = true;
|
|
||||||
} else {
|
|
||||||
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
|
|
||||||
#ifdef _WIN32
|
|
||||||
p.filepath = argv_w != nullptr
|
|
||||||
? Common::UTF16ToUTF8(argv_w[optind])
|
|
||||||
: argv[optind];
|
|
||||||
#else
|
|
||||||
p.filepath = argv[optind];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
optind++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.argv0 = argv[0];
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
|
|
||||||
// apply the log_filter setting
|
|
||||||
// the logger was initialized before and doesn't pick up the filter on its own
|
|
||||||
Common::Log::Filter filter{};
|
|
||||||
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
|
|
||||||
Common::Log::SetGlobalFilter(filter);
|
|
||||||
|
|
||||||
if (!lp.program_args.empty()) {
|
|
||||||
Settings::values.program_args = lp.program_args;
|
|
||||||
}
|
|
||||||
if (!lp.input_profile.empty()) {
|
|
||||||
auto& players = Settings::values.players.GetValue();
|
|
||||||
players[0].profile_name = lp.input_profile;
|
|
||||||
}
|
|
||||||
if (lp.selected_user.has_value()) {
|
|
||||||
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
|
|
||||||
}
|
|
||||||
if (lp.override_gdb_port.has_value()) {
|
|
||||||
Settings::values.use_gdbstub = true;
|
|
||||||
Settings::values.gdbstub_port = *lp.override_gdb_port;
|
|
||||||
}
|
|
||||||
if (lp.force_single_core) {
|
|
||||||
Settings::values.use_multi_core = false;
|
|
||||||
}
|
|
||||||
if (lp.force_null_render) {
|
|
||||||
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lp.print_version) {
|
|
||||||
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
|
|
||||||
}
|
|
||||||
if (lp.print_help) {
|
|
||||||
LOG_INFO(Frontend,
|
|
||||||
"Usage: {}"
|
|
||||||
" [options] <filename>\n"
|
|
||||||
"-c, --config Load the specified configuration file\n"
|
|
||||||
"-f, --fullscreen Start in fullscreen mode\n"
|
|
||||||
"-g, --game File path of the game to load\n"
|
|
||||||
"-h, --help Display this help and exit\n"
|
|
||||||
"-m, --multiplayer=nick:password@address:port"
|
|
||||||
" Nickname, password, address and port for multiplayer\n"
|
|
||||||
"-p, --program Pass following string as arguments to executable\n"
|
|
||||||
"-u, --user Select a specific user profile from 0 to 7\n"
|
|
||||||
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
|
||||||
"-v, --version Output version information and exit\n",
|
|
||||||
lp.argv0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <optional>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "network/room.h"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
class System;
|
|
||||||
struct LaunchParams {
|
|
||||||
std::string argv0{};
|
|
||||||
std::string filepath{};
|
|
||||||
std::string nickname{};
|
|
||||||
std::string password{};
|
|
||||||
std::string address{};
|
|
||||||
std::string input_profile{};
|
|
||||||
std::string program_args{};
|
|
||||||
std::optional<std::string> config_path{};
|
|
||||||
std::optional<int> selected_user{};
|
|
||||||
std::optional<u16> override_gdb_port{};
|
|
||||||
std::optional<std::string> log_filter{};
|
|
||||||
u16 port = Network::DefaultRoomPort;
|
|
||||||
bool use_multiplayer = false;
|
|
||||||
bool fullscreen = false;
|
|
||||||
bool force_null_render = false;
|
|
||||||
bool force_single_core = false;
|
|
||||||
// print
|
|
||||||
bool print_version = false;
|
|
||||||
bool print_help = false;
|
|
||||||
// qt
|
|
||||||
bool launch_hlaunch = false;
|
|
||||||
bool launch_qlaunch = false;
|
|
||||||
bool launch_setup = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
|
|
||||||
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
|
|
||||||
|
|
||||||
}
|
|
||||||
+4
-31
@@ -7,14 +7,12 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <span>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/lz4_compression.h"
|
#include "common/lz4_compression.h"
|
||||||
#include "common/zbic_compression.h"
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -106,36 +104,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||||
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
||||||
if (nso_header.IsSegmentCompressed(i)) {
|
if (nso_header.IsSegmentCompressed(i)) {
|
||||||
if (nso_header.IsZBICCompressed()) {
|
int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]);
|
||||||
// ZBIC compression
|
ASSERT(r == int(nso_header.segments[i].size));
|
||||||
const int r = Common::Compression::DecompressDataZBIC(
|
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size);
|
||||||
std::span<u8>{decompressed_size}.first(nso_header.segments[i].size),
|
|
||||||
std::span<const u8>{compressed_data}.first(nso_header.segments_compressed_size[i])
|
|
||||||
);
|
|
||||||
ASSERT(r > 0);
|
|
||||||
} else {
|
|
||||||
// LZ4 compression
|
|
||||||
int r = Common::Compression::DecompressDataLZ4(
|
|
||||||
decompressed_size.data(),
|
|
||||||
nso_header.segments[i].size,
|
|
||||||
compressed_data.data(),
|
|
||||||
nso_header.segments_compressed_size[i]
|
|
||||||
);
|
|
||||||
ASSERT(r == int(nso_header.segments[i].size));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(
|
|
||||||
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
|
||||||
decompressed_size.data(),
|
|
||||||
nso_header.segments[i].size
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// Not compressed
|
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size);
|
||||||
std::memcpy(
|
|
||||||
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
|
||||||
compressed_data.data(),
|
|
||||||
nso_header.segments[i].size
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
||||||
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -61,9 +58,6 @@ struct NSOHeader {
|
|||||||
std::array<SHA256Hash, 3> segment_hashes;
|
std::array<SHA256Hash, 3> segment_hashes;
|
||||||
|
|
||||||
bool IsSegmentCompressed(size_t segment_num) const;
|
bool IsSegmentCompressed(size_t segment_num) const;
|
||||||
bool IsZBICCompressed() const {
|
|
||||||
return ((flags >> 7) & 1) != 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// gcc14 bug
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "core/memory/dmnt_cheat_types.h"
|
#include "core/memory/dmnt_cheat_types.h"
|
||||||
@@ -1275,8 +1266,3 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core::Memory
|
} // namespace Core::Memory
|
||||||
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
@@ -254,16 +254,14 @@ struct UnrecognizedInstruction {
|
|||||||
|
|
||||||
struct CheatVmOpcode {
|
struct CheatVmOpcode {
|
||||||
bool begin_conditional_block{};
|
bool begin_conditional_block{};
|
||||||
std::variant<
|
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
|
||||||
std::monostate,
|
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
|
||||||
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
|
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
||||||
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
|
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
||||||
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
||||||
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
|
||||||
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
|
||||||
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
|
opcode{};
|
||||||
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
|
|
||||||
> opcode{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DmntCheatVm {
|
class DmntCheatVm {
|
||||||
|
|||||||
@@ -170,19 +170,13 @@ template<>
|
|||||||
|
|
||||||
// check for marked bit, use as unmapped if marked
|
// check for marked bit, use as unmapped if marked
|
||||||
if (ctx.conf.page_table_marked_bit) {
|
if (ctx.conf.page_table_marked_bit) {
|
||||||
auto const marked_bit = *ctx.conf.page_table_marked_bit;
|
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||||
if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) {
|
code.jc(abort, code.T_NEAR);
|
||||||
code.test(page, s32(1 << marked_bit));
|
|
||||||
code.jnz(abort, code.T_NEAR);
|
|
||||||
} else {
|
|
||||||
code.bt(page, marked_bit);
|
|
||||||
code.jc(abort, code.T_NEAR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// mask away attributes
|
// mask away attributes
|
||||||
if (ctx.conf.page_table_pointer_mask == 0) {
|
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||||
code.test(page, page);
|
code.test(page, page);
|
||||||
} else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) {
|
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||||
code.and_(page, ctx.conf.page_table_pointer_mask);
|
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||||
} else {
|
} else {
|
||||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -79,10 +79,8 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
|
|||||||
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
||||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
|
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
|
||||||
if (auto const shfmt = data->shared_memory_format; shfmt) {
|
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
||||||
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
UpdateButtonLifo(npad_entry, data->aruid);
|
||||||
UpdateButtonLifo(npad_entry, data->aruid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,19 +88,19 @@ void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
|
|||||||
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
||||||
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
|
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
|
||||||
if (auto const shfmt = data->shared_memory_format; shfmt) {
|
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
||||||
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
UpdateButtonLifo(npad_entry, data->aruid);
|
||||||
UpdateButtonLifo(npad_entry, data->aruid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
|
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
|
||||||
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
|
||||||
if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) {
|
auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
|
||||||
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
if (data == nullptr) {
|
||||||
UpdateButtonLifo(npad_entry, aruid);
|
return;
|
||||||
}
|
}
|
||||||
|
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
|
||||||
|
UpdateButtonLifo(npad_entry, aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
|
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
|
||||||
|
|||||||
@@ -45,19 +45,6 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
|||||||
AbstractPad{hid_core_.kernel},
|
AbstractPad{hid_core_.kernel},
|
||||||
}}
|
}}
|
||||||
{
|
{
|
||||||
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
|
|
||||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
|
||||||
auto& controller = controller_data[aruid_index][i];
|
|
||||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
|
||||||
Core::HID::ControllerUpdateCallback engine_callback{
|
|
||||||
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
|
||||||
ControllerUpdate(*kernel, type, i);
|
|
||||||
},
|
|
||||||
.is_npad_service = true,
|
|
||||||
};
|
|
||||||
controller.callback_key = controller.device->SetCallback(engine_callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
||||||
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
||||||
}
|
}
|
||||||
@@ -106,6 +93,16 @@ Result NPad::Activate(u64 aruid) {
|
|||||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||||
auto& controller = controller_data[aruid_index][i];
|
auto& controller = controller_data[aruid_index][i];
|
||||||
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
||||||
|
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||||
|
if (!controller.callback_key) {
|
||||||
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
|
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
||||||
|
ControllerUpdate(hid_core.kernel, type, i);
|
||||||
|
},
|
||||||
|
.is_npad_service = true,
|
||||||
|
};
|
||||||
|
controller.callback_key = controller.device->SetCallback(engine_callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefill controller buffers
|
// Prefill controller buffers
|
||||||
|
|||||||
@@ -226,7 +226,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting."));
|
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
||||||
|
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||||
|
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||||
tr("May reduce shader stutter."));
|
tr("May reduce shader stutter."));
|
||||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||||
@@ -440,6 +442,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
|||||||
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
||||||
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
||||||
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
||||||
|
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
|
||||||
}});
|
}});
|
||||||
translations->insert(
|
translations->insert(
|
||||||
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
||||||
|
|||||||
@@ -66,17 +66,14 @@ void EmuThread::run() {
|
|||||||
} else {
|
} else {
|
||||||
QtCommon::system->Pause();
|
QtCommon::system->Pause();
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
|
|
||||||
EmulationPaused(lk);
|
EmulationPaused(lk);
|
||||||
m_should_run_cv.wait(lk, stop_token, [&] {
|
m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
|
||||||
return m_should_run;
|
|
||||||
});
|
|
||||||
EmulationResumed(lk);
|
EmulationResumed(lk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown the main emulated process
|
// Shutdown the main emulated process
|
||||||
// Needs to pause first, we may not have paused before shutting down.
|
|
||||||
QtCommon::system->Pause();
|
|
||||||
QtCommon::system->DetachDebugger();
|
QtCommon::system->DetachDebugger();
|
||||||
QtCommon::system->ShutdownMainProcess();
|
QtCommon::system->ShutdownMainProcess();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -31,6 +28,11 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
|||||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <auto func, typename... Args>
|
||||||
|
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||||
|
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ArgType>
|
template <typename ArgType>
|
||||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||||
@@ -51,10 +53,21 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
|||||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||||
using Traits = FuncTraits<decltype(func)>;
|
using Traits = FuncTraits<decltype(func)>;
|
||||||
if constexpr (is_first_arg_inst) {
|
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
||||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
if constexpr (is_first_arg_inst) {
|
||||||
|
SetDefinition<func>(
|
||||||
|
ctx, inst, *inst,
|
||||||
|
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||||
|
} else {
|
||||||
|
SetDefinition<func>(
|
||||||
|
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
if constexpr (is_first_arg_inst) {
|
||||||
|
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||||
|
} else {
|
||||||
|
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user