mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 19:26:52 +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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,8 +11,8 @@
|
|||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioManager::AudioManager() {
|
AudioManager::AudioManager(Core::System& system) {
|
||||||
thread = std::jthread([this](std::stop_token stop_token) {
|
thread = std::jthread([&](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("AudioManager");
|
Common::SetCurrentThreadName("AudioManager");
|
||||||
std::unique_lock l{events.GetAudioEventLock()};
|
std::unique_lock l{events.GetAudioEventLock()};
|
||||||
events.ClearEvents();
|
events.ClearEvents();
|
||||||
@@ -25,7 +25,7 @@ AudioManager::AudioManager() {
|
|||||||
const auto event_type = Event::Type(i);
|
const auto event_type = Event::Type(i);
|
||||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||||
if (buffer_events[i]) {
|
if (buffer_events[i]) {
|
||||||
buffer_events[i]();
|
buffer_events[i](buffer_data[i], system);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events.SetAudioEvent(event_type, false);
|
events.SetAudioEvent(event_type, false);
|
||||||
@@ -42,12 +42,13 @@ void AudioManager::Shutdown() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||||
if (buffer_events[index] == nullptr) {
|
if (buffer_events[index] == nullptr) {
|
||||||
buffer_events[index] = std::move(buffer_func);
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
buffer_data[index] = data;
|
||||||
needs_update = true;
|
needs_update = true;
|
||||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||||
}
|
}
|
||||||
@@ -56,12 +57,13 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
|||||||
return Service::Audio::ResultOperationFailed;
|
return Service::Audio::ResultOperationFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) {
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||||
if (buffer_events[index] == nullptr) {
|
if (buffer_events[index] == nullptr) {
|
||||||
buffer_events[index] = std::move(buffer_func);
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
buffer_data[index] = data;
|
||||||
needs_update = true;
|
needs_update = true;
|
||||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
#include "audio_core/audio_event.h"
|
#include "audio_core/audio_event.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
union Result;
|
union Result;
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
@@ -34,31 +38,24 @@ namespace AudioCore {
|
|||||||
* This is only used by audio in and audio out.
|
* This is only used by audio in and audio out.
|
||||||
*/
|
*/
|
||||||
class AudioManager {
|
class AudioManager {
|
||||||
using BufferEventFunc = std::function<void()>;
|
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AudioManager();
|
explicit AudioManager(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown the audio manager.
|
* Shutdown the audio manager.
|
||||||
*/
|
*/
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/**
|
/// Register the out manager, keeping a function to be called when the out event is signalled.
|
||||||
* Register the out manager, keeping a function to be called when the out event is signalled.
|
/// @param buffer_func - Function to be called on signal.
|
||||||
*
|
/// @return Result code.
|
||||||
* @param buffer_func - Function to be called on signal.
|
Result SetOutManager(void *data, BufferEventFunc buffer_func);
|
||||||
* @return Result code.
|
|
||||||
*/
|
|
||||||
Result SetOutManager(BufferEventFunc buffer_func);
|
|
||||||
|
|
||||||
/**
|
/// Register the in manager, keeping a function to be called when the in event is signalled.
|
||||||
* Register the in manager, keeping a function to be called when the in event is signalled.
|
/// @param buffer_func - Function to be called on signal.
|
||||||
*
|
/// @return Result code.
|
||||||
* @param buffer_func - Function to be called on signal.
|
Result SetInManager(void *data, BufferEventFunc buffer_func);
|
||||||
* @return Result code.
|
|
||||||
*/
|
|
||||||
Result SetInManager(BufferEventFunc buffer_func);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an event to signalled, and signal the thread.
|
* Set an event to signalled, and signal the thread.
|
||||||
@@ -73,8 +70,9 @@ private:
|
|||||||
bool needs_update{};
|
bool needs_update{};
|
||||||
/// Events to be set and signalled
|
/// Events to be set and signalled
|
||||||
Event events{};
|
Event events{};
|
||||||
/// Callbacks for each manager
|
/// Callbacks (and user data) for each manager
|
||||||
std::array<BufferEventFunc, 3> buffer_events{};
|
std::array<BufferEventFunc, 3> buffer_events{};
|
||||||
|
std::array<void*, 3> buffer_data{};
|
||||||
/// General lock
|
/// General lock
|
||||||
std::mutex lock{};
|
std::mutex lock{};
|
||||||
/// Main thread for waiting and callbacks
|
/// Main thread for waiting and callbacks
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -100,8 +100,9 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
|
: core{core_}
|
||||||
adsp_rendered_event{adsp_rendered_event_} {}
|
, adsp_rendered_event{adsp_rendered_event_}
|
||||||
|
{}
|
||||||
|
|
||||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||||
@@ -406,7 +407,7 @@ void System::Finalize() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active) {
|
if (IsActive()) {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,14 +434,12 @@ void System::Start() {
|
|||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
frames_elapsed = 0;
|
frames_elapsed = 0;
|
||||||
state = State::Started;
|
state = State::Started;
|
||||||
active = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::Stop() {
|
void System::Stop() {
|
||||||
{
|
{
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
state = State::Stopped;
|
state = State::Stopped;
|
||||||
active = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execution_mode == ExecutionMode::Auto) {
|
if (execution_mode == ExecutionMode::Auto) {
|
||||||
@@ -480,8 +479,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
|
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer, memory_pool_count);
|
||||||
memory_pool_count);
|
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
||||||
return result;
|
return result;
|
||||||
@@ -582,16 +580,16 @@ u32 System::GetRenderingDevice() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsActive() const {
|
bool System::IsActive() const {
|
||||||
return active;
|
return state == State::Started;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SendCommandToDsp() {
|
void System::SendCommandToDsp() {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
|
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
if (active) {
|
if (IsActive()) {
|
||||||
terminate_event.Reset();
|
terminate_event.Reset();
|
||||||
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
|
const auto remaining_command_count = audio_renderer.GetRemainCommandCount(session_id);
|
||||||
u64 command_size{0};
|
u64 command_size{0};
|
||||||
|
|
||||||
if (remaining_command_count) {
|
if (remaining_command_count) {
|
||||||
@@ -738,7 +736,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
|||||||
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
||||||
total_ticks_elapsed += end_time - start_time;
|
total_ticks_elapsed += end_time - start_time;
|
||||||
num_command_lists_generated++;
|
num_command_lists_generated++;
|
||||||
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
|
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
|
||||||
frames_elapsed++;
|
frames_elapsed++;
|
||||||
|
|
||||||
return command_buffer.size;
|
return command_buffer.size;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -219,12 +222,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& core;
|
Core::System& core;
|
||||||
/// Reference to the ADSP's AudioRenderer for communication
|
|
||||||
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
|
||||||
/// Is this system initialized?
|
/// Is this system initialized?
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
/// Is this system currently active?
|
|
||||||
std::atomic<bool> active{};
|
|
||||||
/// State of the system
|
/// State of the system
|
||||||
State state{State::Stopped};
|
State state{State::Stopped};
|
||||||
/// Sample rate for the system
|
/// Sample rate for the system
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -16,22 +16,22 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
SystemManager::SystemManager(Core::System& core_)
|
SystemManager::SystemManager(Core::System& core_)
|
||||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
|
: core{core_}
|
||||||
|
{}
|
||||||
|
|
||||||
SystemManager::~SystemManager() {
|
SystemManager::~SystemManager() {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::InitializeUnsafe() {
|
void SystemManager::InitializeUnsafe() {
|
||||||
if (!active) {
|
if (!thread.joinable()) {
|
||||||
active = true;
|
core.AudioCore().ADSP().AudioRenderer().Start();
|
||||||
audio_renderer.Start();
|
|
||||||
thread = std::jthread([this](std::stop_token stop_token) {
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||||
while (active && !stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
{
|
{
|
||||||
std::scoped_lock l{mutex1};
|
std::scoped_lock lk{mutex};
|
||||||
for (auto system : systems)
|
for (auto system : systems)
|
||||||
system->SendCommandToDsp();
|
system->SendCommandToDsp();
|
||||||
}
|
}
|
||||||
@@ -43,39 +43,31 @@ void SystemManager::InitializeUnsafe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::Stop() {
|
void SystemManager::Stop() {
|
||||||
if (active) {
|
if (thread.joinable()) {
|
||||||
active = false;
|
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
thread.join();
|
thread.join();
|
||||||
audio_renderer.Stop();
|
core.AudioCore().ADSP().AudioRenderer().Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Add(System& system_) {
|
bool SystemManager::Add(System& system_) {
|
||||||
std::scoped_lock l2{mutex2};
|
std::scoped_lock lk{mutex};
|
||||||
if (systems.size() + 1 > MaxRendererSessions) {
|
if (systems.size() + 1 > MaxRendererSessions) {
|
||||||
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
{
|
if (systems.empty())
|
||||||
std::scoped_lock l{mutex1};
|
InitializeUnsafe();
|
||||||
if (systems.empty())
|
|
||||||
InitializeUnsafe();
|
|
||||||
}
|
|
||||||
systems.push_back(&system_);
|
systems.push_back(&system_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Remove(System& system_) {
|
bool SystemManager::Remove(System& system_) {
|
||||||
std::scoped_lock l2{mutex2};
|
std::scoped_lock lk{mutex};
|
||||||
{
|
if (systems.remove(&system_) == 0) {
|
||||||
std::scoped_lock l{mutex1};
|
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
||||||
if (systems.remove(&system_) == 0) {
|
return false;
|
||||||
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systems.empty())
|
if (systems.empty())
|
||||||
Stop();
|
Stop();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -75,14 +75,8 @@ private:
|
|||||||
std::list<System*> systems{};
|
std::list<System*> systems{};
|
||||||
/// Main worker thread for generating command lists
|
/// Main worker thread for generating command lists
|
||||||
std::jthread thread;
|
std::jthread thread;
|
||||||
/// Mutex for the systems
|
/// Mutex for the systems list
|
||||||
std::mutex mutex1{};
|
std::mutex mutex{};
|
||||||
/// Mutex for adding/removing systems
|
|
||||||
std::mutex mutex2{};
|
|
||||||
/// Is the system manager thread active?
|
|
||||||
std::atomic<bool> active{};
|
|
||||||
/// Reference to the ADSP's AudioRenderer for communication
|
|
||||||
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ add_library(
|
|||||||
fs/fs_util.h
|
fs/fs_util.h
|
||||||
fs/path_util.cpp
|
fs/path_util.cpp
|
||||||
fs/path_util.h
|
fs/path_util.h
|
||||||
|
hash.h
|
||||||
heap_tracker.cpp
|
heap_tracker.cpp
|
||||||
heap_tracker.h
|
heap_tracker.h
|
||||||
hex_util.cpp
|
hex_util.cpp
|
||||||
@@ -75,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
|
||||||
@@ -136,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
|
||||||
@@ -148,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)
|
||||||
|
|||||||
+251
-252
@@ -12,94 +12,93 @@
|
|||||||
#include "common/android/multiplayer/multiplayer.h"
|
#include "common/android/multiplayer/multiplayer.h"
|
||||||
#include <network/network.h>
|
#include <network/network.h>
|
||||||
|
|
||||||
static struct {
|
|
||||||
JavaVM *java_vm;
|
|
||||||
jclass native_library_class;
|
|
||||||
jclass disk_cache_progress_class;
|
|
||||||
jclass load_callback_stage_class;
|
|
||||||
jclass game_dir_class;
|
|
||||||
jmethodID game_dir_constructor;
|
|
||||||
jmethodID exit_emulation_activity;
|
|
||||||
jmethodID disk_cache_load_progress;
|
|
||||||
jmethodID on_emulation_started;
|
|
||||||
jmethodID on_emulation_stopped;
|
|
||||||
jmethodID on_program_changed;
|
|
||||||
jmethodID copy_to_storage;
|
|
||||||
jmethodID file_exists;
|
|
||||||
jmethodID file_extension;
|
|
||||||
|
|
||||||
jclass game_class;
|
static JavaVM *s_java_vm;
|
||||||
jmethodID game_constructor;
|
static jclass s_native_library_class;
|
||||||
jfieldID game_title_field;
|
static jclass s_disk_cache_progress_class;
|
||||||
jfieldID game_path_field;
|
static jclass s_load_callback_stage_class;
|
||||||
jfieldID game_program_id_field;
|
static jclass s_game_dir_class;
|
||||||
jfieldID game_developer_field;
|
static jmethodID s_game_dir_constructor;
|
||||||
jfieldID game_version_field;
|
static jmethodID s_exit_emulation_activity;
|
||||||
jfieldID game_is_homebrew_field;
|
static jmethodID s_disk_cache_load_progress;
|
||||||
|
static jmethodID s_on_emulation_started;
|
||||||
|
static jmethodID s_on_emulation_stopped;
|
||||||
|
static jmethodID s_on_program_changed;
|
||||||
|
static jmethodID s_copy_to_storage;
|
||||||
|
static jmethodID s_file_exists;
|
||||||
|
static jmethodID s_file_extension;
|
||||||
|
|
||||||
jclass string_class;
|
static jclass s_game_class;
|
||||||
jclass pair_class;
|
static jmethodID s_game_constructor;
|
||||||
jmethodID pair_constructor;
|
static jfieldID s_game_title_field;
|
||||||
jfieldID pair_first_field;
|
static jfieldID s_game_path_field;
|
||||||
jfieldID pair_second_field;
|
static jfieldID s_game_program_id_field;
|
||||||
|
static jfieldID s_game_developer_field;
|
||||||
|
static jfieldID s_game_version_field;
|
||||||
|
static jfieldID s_game_is_homebrew_field;
|
||||||
|
|
||||||
jclass overlay_control_data_class;
|
static jclass s_string_class;
|
||||||
jmethodID overlay_control_data_constructor;
|
static jclass s_pair_class;
|
||||||
jfieldID overlay_control_data_id_field;
|
static jmethodID s_pair_constructor;
|
||||||
jfieldID overlay_control_data_enabled_field;
|
static jfieldID s_pair_first_field;
|
||||||
jfieldID overlay_control_data_individual_scale_field;
|
static jfieldID s_pair_second_field;
|
||||||
jfieldID overlay_control_data_landscape_position_field;
|
|
||||||
jfieldID overlay_control_data_portrait_position_field;
|
|
||||||
jfieldID overlay_control_data_foldable_position_field;
|
|
||||||
|
|
||||||
jclass patch_class;
|
static jclass s_overlay_control_data_class;
|
||||||
jmethodID patch_constructor;
|
static jmethodID s_overlay_control_data_constructor;
|
||||||
jfieldID patch_enabled_field;
|
static jfieldID s_overlay_control_data_id_field;
|
||||||
jfieldID patch_name_field;
|
static jfieldID s_overlay_control_data_enabled_field;
|
||||||
jfieldID patch_version_field;
|
static jfieldID s_overlay_control_data_individual_scale_field;
|
||||||
jfieldID patch_type_field;
|
static jfieldID s_overlay_control_data_landscape_position_field;
|
||||||
jfieldID patch_program_id_field;
|
static jfieldID s_overlay_control_data_portrait_position_field;
|
||||||
jfieldID patch_title_id_field;
|
static jfieldID s_overlay_control_data_foldable_position_field;
|
||||||
|
|
||||||
jclass double_class;
|
static jclass s_patch_class;
|
||||||
jmethodID double_constructor;
|
static jmethodID s_patch_constructor;
|
||||||
jmethodID double_value_method;
|
static jfieldID s_patch_enabled_field;
|
||||||
|
static jfieldID s_patch_name_field;
|
||||||
|
static jfieldID s_patch_version_field;
|
||||||
|
static jfieldID s_patch_type_field;
|
||||||
|
static jfieldID s_patch_program_id_field;
|
||||||
|
static jfieldID s_patch_title_id_field;
|
||||||
|
|
||||||
jclass integer_class;
|
static jclass s_double_class;
|
||||||
jmethodID integer_constructor;
|
static jmethodID s_double_constructor;
|
||||||
jmethodID integer_value_method;
|
static jmethodID s_double_value_method;
|
||||||
|
|
||||||
jclass boolean_class;
|
static jclass s_integer_class;
|
||||||
jmethodID boolean_constructor;
|
static jmethodID s_integer_constructor;
|
||||||
jmethodID boolean_value_method;
|
static jmethodID s_integer_value_method;
|
||||||
|
|
||||||
jclass player_input_class;
|
static jclass s_boolean_class;
|
||||||
jmethodID player_input_constructor;
|
static jmethodID s_boolean_constructor;
|
||||||
jfieldID player_input_connected_field;
|
static jmethodID s_boolean_value_method;
|
||||||
jfieldID player_input_buttons_field;
|
|
||||||
jfieldID player_input_analogs_field;
|
|
||||||
jfieldID player_input_motions_field;
|
|
||||||
jfieldID player_input_vibration_enabled_field;
|
|
||||||
jfieldID player_input_vibration_strength_field;
|
|
||||||
jfieldID player_input_body_color_left_field;
|
|
||||||
jfieldID player_input_body_color_right_field;
|
|
||||||
jfieldID player_input_button_color_left_field;
|
|
||||||
jfieldID player_input_button_color_right_field;
|
|
||||||
jfieldID player_input_profile_name_field;
|
|
||||||
jfieldID player_input_use_system_vibrator_field;
|
|
||||||
|
|
||||||
jclass yuzu_input_device_interface;
|
static jclass s_player_input_class;
|
||||||
jmethodID yuzu_input_device_get_name;
|
static jmethodID s_player_input_constructor;
|
||||||
jmethodID yuzu_input_device_get_guid;
|
static jfieldID s_player_input_connected_field;
|
||||||
jmethodID yuzu_input_device_get_port;
|
static jfieldID s_player_input_buttons_field;
|
||||||
jmethodID yuzu_input_device_get_supports_vibration;
|
static jfieldID s_player_input_analogs_field;
|
||||||
jmethodID yuzu_input_device_vibrate;
|
static jfieldID s_player_input_motions_field;
|
||||||
jmethodID yuzu_input_device_get_axes;
|
static jfieldID s_player_input_vibration_enabled_field;
|
||||||
jmethodID yuzu_input_device_has_keys;
|
static jfieldID s_player_input_vibration_strength_field;
|
||||||
|
static jfieldID s_player_input_body_color_left_field;
|
||||||
|
static jfieldID s_player_input_body_color_right_field;
|
||||||
|
static jfieldID s_player_input_button_color_left_field;
|
||||||
|
static jfieldID s_player_input_button_color_right_field;
|
||||||
|
static jfieldID s_player_input_profile_name_field;
|
||||||
|
static jfieldID s_player_input_use_system_vibrator_field;
|
||||||
|
|
||||||
jmethodID add_netplay_message;
|
static jclass s_yuzu_input_device_interface;
|
||||||
jmethodID clear_chat;
|
static jmethodID s_yuzu_input_device_get_name;
|
||||||
} state;
|
static jmethodID s_yuzu_input_device_get_guid;
|
||||||
|
static jmethodID s_yuzu_input_device_get_port;
|
||||||
|
static jmethodID s_yuzu_input_device_get_supports_vibration;
|
||||||
|
static jmethodID s_yuzu_input_device_vibrate;
|
||||||
|
static jmethodID s_yuzu_input_device_get_axes;
|
||||||
|
static jmethodID s_yuzu_input_device_has_keys;
|
||||||
|
|
||||||
|
static jmethodID s_add_netplay_message;
|
||||||
|
static jmethodID s_clear_chat;
|
||||||
|
|
||||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
@@ -107,14 +106,14 @@ namespace Common::Android {
|
|||||||
JNIEnv *GetEnvForThread() {
|
JNIEnv *GetEnvForThread() {
|
||||||
thread_local static struct OwnedEnv {
|
thread_local static struct OwnedEnv {
|
||||||
OwnedEnv() {
|
OwnedEnv() {
|
||||||
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
state.java_vm->AttachCurrentThread(&env, nullptr);
|
s_java_vm->AttachCurrentThread(&env, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
~OwnedEnv() {
|
~OwnedEnv() {
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
state.java_vm->DetachCurrentThread();
|
s_java_vm->DetachCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
@@ -124,303 +123,303 @@ namespace Common::Android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jclass GetNativeLibraryClass() {
|
jclass GetNativeLibraryClass() {
|
||||||
return state.native_library_class;
|
return s_native_library_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheProgressClass() {
|
jclass GetDiskCacheProgressClass() {
|
||||||
return state.disk_cache_progress_class;
|
return s_disk_cache_progress_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheLoadCallbackStageClass() {
|
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||||
return state.load_callback_stage_class;
|
return s_load_callback_stage_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameDirClass() {
|
jclass GetGameDirClass() {
|
||||||
return state.game_dir_class;
|
return s_game_dir_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameDirConstructor() {
|
jmethodID GetGameDirConstructor() {
|
||||||
return state.game_dir_constructor;
|
return s_game_dir_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetExitEmulationActivity() {
|
jmethodID GetExitEmulationActivity() {
|
||||||
return state.exit_emulation_activity;
|
return s_exit_emulation_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDiskCacheLoadProgress() {
|
jmethodID GetDiskCacheLoadProgress() {
|
||||||
return state.disk_cache_load_progress;
|
return s_disk_cache_load_progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetCopyToStorage() {
|
jmethodID GetCopyToStorage() {
|
||||||
return state.copy_to_storage;
|
return s_copy_to_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExists() {
|
jmethodID GetFileExists() {
|
||||||
return state.file_exists;
|
return s_file_exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExtension() {
|
jmethodID GetFileExtension() {
|
||||||
return state.file_extension;
|
return s_file_extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStarted() {
|
jmethodID GetOnEmulationStarted() {
|
||||||
return state.on_emulation_started;
|
return s_on_emulation_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStopped() {
|
jmethodID GetOnEmulationStopped() {
|
||||||
return state.on_emulation_stopped;
|
return s_on_emulation_stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnProgramChanged() {
|
jmethodID GetOnProgramChanged() {
|
||||||
return state.on_program_changed;
|
return s_on_program_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameClass() {
|
jclass GetGameClass() {
|
||||||
return state.game_class;
|
return s_game_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameConstructor() {
|
jmethodID GetGameConstructor() {
|
||||||
return state.game_constructor;
|
return s_game_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameTitleField() {
|
jfieldID GetGameTitleField() {
|
||||||
return state.game_title_field;
|
return s_game_title_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGamePathField() {
|
jfieldID GetGamePathField() {
|
||||||
return state.game_path_field;
|
return s_game_path_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameProgramIdField() {
|
jfieldID GetGameProgramIdField() {
|
||||||
return state.game_program_id_field;
|
return s_game_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameDeveloperField() {
|
jfieldID GetGameDeveloperField() {
|
||||||
return state.game_developer_field;
|
return s_game_developer_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameVersionField() {
|
jfieldID GetGameVersionField() {
|
||||||
return state.game_version_field;
|
return s_game_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameIsHomebrewField() {
|
jfieldID GetGameIsHomebrewField() {
|
||||||
return state.game_is_homebrew_field;
|
return s_game_is_homebrew_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetStringClass() {
|
jclass GetStringClass() {
|
||||||
return state.string_class;
|
return s_string_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPairClass() {
|
jclass GetPairClass() {
|
||||||
return state.pair_class;
|
return s_pair_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPairConstructor() {
|
jmethodID GetPairConstructor() {
|
||||||
return state.pair_constructor;
|
return s_pair_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairFirstField() {
|
jfieldID GetPairFirstField() {
|
||||||
return state.pair_first_field;
|
return s_pair_first_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairSecondField() {
|
jfieldID GetPairSecondField() {
|
||||||
return state.pair_second_field;
|
return s_pair_second_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetOverlayControlDataClass() {
|
jclass GetOverlayControlDataClass() {
|
||||||
return state.overlay_control_data_class;
|
return s_overlay_control_data_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOverlayControlDataConstructor() {
|
jmethodID GetOverlayControlDataConstructor() {
|
||||||
return state.overlay_control_data_constructor;
|
return s_overlay_control_data_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIdField() {
|
jfieldID GetOverlayControlDataIdField() {
|
||||||
return state.overlay_control_data_id_field;
|
return s_overlay_control_data_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataEnabledField() {
|
jfieldID GetOverlayControlDataEnabledField() {
|
||||||
return state.overlay_control_data_enabled_field;
|
return s_overlay_control_data_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIndividualScaleField() {
|
jfieldID GetOverlayControlDataIndividualScaleField() {
|
||||||
return state.overlay_control_data_individual_scale_field;
|
return s_overlay_control_data_individual_scale_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataLandscapePositionField() {
|
jfieldID GetOverlayControlDataLandscapePositionField() {
|
||||||
return state.overlay_control_data_landscape_position_field;
|
return s_overlay_control_data_landscape_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataPortraitPositionField() {
|
jfieldID GetOverlayControlDataPortraitPositionField() {
|
||||||
return state.overlay_control_data_portrait_position_field;
|
return s_overlay_control_data_portrait_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataFoldablePositionField() {
|
jfieldID GetOverlayControlDataFoldablePositionField() {
|
||||||
return state.overlay_control_data_foldable_position_field;
|
return s_overlay_control_data_foldable_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPatchClass() {
|
jclass GetPatchClass() {
|
||||||
return state.patch_class;
|
return s_patch_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPatchConstructor() {
|
jmethodID GetPatchConstructor() {
|
||||||
return state.patch_constructor;
|
return s_patch_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchEnabledField() {
|
jfieldID GetPatchEnabledField() {
|
||||||
return state.patch_enabled_field;
|
return s_patch_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchNameField() {
|
jfieldID GetPatchNameField() {
|
||||||
return state.patch_name_field;
|
return s_patch_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchVersionField() {
|
jfieldID GetPatchVersionField() {
|
||||||
return state.patch_version_field;
|
return s_patch_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTypeField() {
|
jfieldID GetPatchTypeField() {
|
||||||
return state.patch_type_field;
|
return s_patch_type_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchProgramIdField() {
|
jfieldID GetPatchProgramIdField() {
|
||||||
return state.patch_program_id_field;
|
return s_patch_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTitleIdField() {
|
jfieldID GetPatchTitleIdField() {
|
||||||
return state.patch_title_id_field;
|
return s_patch_title_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDoubleClass() {
|
jclass GetDoubleClass() {
|
||||||
return state.double_class;
|
return s_double_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleConstructor() {
|
jmethodID GetDoubleConstructor() {
|
||||||
return state.double_constructor;
|
return s_double_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleValueMethod() {
|
jmethodID GetDoubleValueMethod() {
|
||||||
return state.double_value_method;
|
return s_double_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetIntegerClass() {
|
jclass GetIntegerClass() {
|
||||||
return state.integer_class;
|
return s_integer_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerConstructor() {
|
jmethodID GetIntegerConstructor() {
|
||||||
return state.integer_constructor;
|
return s_integer_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerValueMethod() {
|
jmethodID GetIntegerValueMethod() {
|
||||||
return state.integer_value_method;
|
return s_integer_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetBooleanClass() {
|
jclass GetBooleanClass() {
|
||||||
return state.boolean_class;
|
return s_boolean_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanConstructor() {
|
jmethodID GetBooleanConstructor() {
|
||||||
return state.boolean_constructor;
|
return s_boolean_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanValueMethod() {
|
jmethodID GetBooleanValueMethod() {
|
||||||
return state.boolean_value_method;
|
return s_boolean_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPlayerInputClass() {
|
jclass GetPlayerInputClass() {
|
||||||
return state.player_input_class;
|
return s_player_input_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPlayerInputConstructor() {
|
jmethodID GetPlayerInputConstructor() {
|
||||||
return state.player_input_constructor;
|
return s_player_input_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputConnectedField() {
|
jfieldID GetPlayerInputConnectedField() {
|
||||||
return state.player_input_connected_field;
|
return s_player_input_connected_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonsField() {
|
jfieldID GetPlayerInputButtonsField() {
|
||||||
return state.player_input_buttons_field;
|
return s_player_input_buttons_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputAnalogsField() {
|
jfieldID GetPlayerInputAnalogsField() {
|
||||||
return state.player_input_analogs_field;
|
return s_player_input_analogs_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputMotionsField() {
|
jfieldID GetPlayerInputMotionsField() {
|
||||||
return state.player_input_motions_field;
|
return s_player_input_motions_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationEnabledField() {
|
jfieldID GetPlayerInputVibrationEnabledField() {
|
||||||
return state.player_input_vibration_enabled_field;
|
return s_player_input_vibration_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationStrengthField() {
|
jfieldID GetPlayerInputVibrationStrengthField() {
|
||||||
return state.player_input_vibration_strength_field;
|
return s_player_input_vibration_strength_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorLeftField() {
|
jfieldID GetPlayerInputBodyColorLeftField() {
|
||||||
return state.player_input_body_color_left_field;
|
return s_player_input_body_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorRightField() {
|
jfieldID GetPlayerInputBodyColorRightField() {
|
||||||
return state.player_input_body_color_right_field;
|
return s_player_input_body_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorLeftField() {
|
jfieldID GetPlayerInputButtonColorLeftField() {
|
||||||
return state.player_input_button_color_left_field;
|
return s_player_input_button_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorRightField() {
|
jfieldID GetPlayerInputButtonColorRightField() {
|
||||||
return state.player_input_button_color_right_field;
|
return s_player_input_button_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputProfileNameField() {
|
jfieldID GetPlayerInputProfileNameField() {
|
||||||
return state.player_input_profile_name_field;
|
return s_player_input_profile_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputUseSystemVibratorField() {
|
jfieldID GetPlayerInputUseSystemVibratorField() {
|
||||||
return state.player_input_use_system_vibrator_field;
|
return s_player_input_use_system_vibrator_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetYuzuInputDeviceInterface() {
|
jclass GetYuzuInputDeviceInterface() {
|
||||||
return state.yuzu_input_device_interface;
|
return s_yuzu_input_device_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetName() {
|
jmethodID GetYuzuDeviceGetName() {
|
||||||
return state.yuzu_input_device_get_name;
|
return s_yuzu_input_device_get_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetGUID() {
|
jmethodID GetYuzuDeviceGetGUID() {
|
||||||
return state.yuzu_input_device_get_guid;
|
return s_yuzu_input_device_get_guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetPort() {
|
jmethodID GetYuzuDeviceGetPort() {
|
||||||
return state.yuzu_input_device_get_port;
|
return s_yuzu_input_device_get_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
||||||
return state.yuzu_input_device_get_supports_vibration;
|
return s_yuzu_input_device_get_supports_vibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceVibrate() {
|
jmethodID GetYuzuDeviceVibrate() {
|
||||||
return state.yuzu_input_device_vibrate;
|
return s_yuzu_input_device_vibrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetAxes() {
|
jmethodID GetYuzuDeviceGetAxes() {
|
||||||
return state.yuzu_input_device_get_axes;
|
return s_yuzu_input_device_get_axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceHasKeys() {
|
jmethodID GetYuzuDeviceHasKeys() {
|
||||||
return state.yuzu_input_device_has_keys;
|
return s_yuzu_input_device_has_keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetAddNetPlayMessage() {
|
jmethodID GetAddNetPlayMessage() {
|
||||||
return state.add_netplay_message;
|
return s_add_netplay_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID ClearChat() {
|
jmethodID ClearChat() {
|
||||||
return state.clear_chat;
|
return s_clear_chat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -437,20 +436,20 @@ namespace Common::Android {
|
|||||||
|
|
||||||
// UnInitialize Android Storage
|
// UnInitialize Android Storage
|
||||||
Common::FS::Android::UnRegisterCallbacks();
|
Common::FS::Android::UnRegisterCallbacks();
|
||||||
env->DeleteGlobalRef(state.native_library_class);
|
env->DeleteGlobalRef(s_native_library_class);
|
||||||
env->DeleteGlobalRef(state.disk_cache_progress_class);
|
env->DeleteGlobalRef(s_disk_cache_progress_class);
|
||||||
env->DeleteGlobalRef(state.load_callback_stage_class);
|
env->DeleteGlobalRef(s_load_callback_stage_class);
|
||||||
env->DeleteGlobalRef(state.game_dir_class);
|
env->DeleteGlobalRef(s_game_dir_class);
|
||||||
env->DeleteGlobalRef(state.game_class);
|
env->DeleteGlobalRef(s_game_class);
|
||||||
env->DeleteGlobalRef(state.string_class);
|
env->DeleteGlobalRef(s_string_class);
|
||||||
env->DeleteGlobalRef(state.pair_class);
|
env->DeleteGlobalRef(s_pair_class);
|
||||||
env->DeleteGlobalRef(state.overlay_control_data_class);
|
env->DeleteGlobalRef(s_overlay_control_data_class);
|
||||||
env->DeleteGlobalRef(state.patch_class);
|
env->DeleteGlobalRef(s_patch_class);
|
||||||
env->DeleteGlobalRef(state.double_class);
|
env->DeleteGlobalRef(s_double_class);
|
||||||
env->DeleteGlobalRef(state.integer_class);
|
env->DeleteGlobalRef(s_integer_class);
|
||||||
env->DeleteGlobalRef(state.boolean_class);
|
env->DeleteGlobalRef(s_boolean_class);
|
||||||
env->DeleteGlobalRef(state.player_input_class);
|
env->DeleteGlobalRef(s_player_input_class);
|
||||||
env->DeleteGlobalRef(state.yuzu_input_device_interface);
|
env->DeleteGlobalRef(s_yuzu_input_device_interface);
|
||||||
|
|
||||||
// UnInitialize applets
|
// UnInitialize applets
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
@@ -464,7 +463,7 @@ namespace Common::Android {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Initialize(JavaVM* vm, JNIEnv *env) {
|
void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||||
state.java_vm = vm;
|
s_java_vm = vm;
|
||||||
InitFFmpegOnLoad(vm);
|
InitFFmpegOnLoad(vm);
|
||||||
|
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
@@ -473,169 +472,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
|
|||||||
|
|
||||||
// Initialize Java classes
|
// Initialize Java classes
|
||||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||||
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||||
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||||
|
|
||||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||||
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||||
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;Z)V");
|
||||||
env->DeleteLocalRef(game_dir_class);
|
env->DeleteLocalRef(game_dir_class);
|
||||||
|
|
||||||
// Initialize methods
|
// Initialize methods
|
||||||
state.exit_emulation_activity =
|
s_exit_emulation_activity =
|
||||||
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V");
|
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
||||||
state.disk_cache_load_progress =
|
s_disk_cache_load_progress =
|
||||||
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V");
|
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
||||||
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage",
|
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||||
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
|
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
||||||
"(Ljava/lang/String;)Z");
|
"(Ljava/lang/String;)Z");
|
||||||
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
|
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
||||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||||
state.on_emulation_started =
|
s_on_emulation_started =
|
||||||
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V");
|
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
||||||
state.on_emulation_stopped =
|
s_on_emulation_stopped =
|
||||||
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V");
|
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
||||||
state.on_program_changed =
|
s_on_program_changed =
|
||||||
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V");
|
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
||||||
|
|
||||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||||
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||||
state.game_constructor = env->GetMethodID(game_class, "<init>",
|
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||||
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||||
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||||
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||||
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||||
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||||
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||||
env->DeleteLocalRef(game_class);
|
env->DeleteLocalRef(game_class);
|
||||||
|
|
||||||
const jclass string_class = env->FindClass("java/lang/String");
|
const jclass string_class = env->FindClass("java/lang/String");
|
||||||
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||||
env->DeleteLocalRef(string_class);
|
env->DeleteLocalRef(string_class);
|
||||||
|
|
||||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||||
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||||
state.pair_constructor =
|
s_pair_constructor =
|
||||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||||
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||||
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||||
env->DeleteLocalRef(pair_class);
|
env->DeleteLocalRef(pair_class);
|
||||||
|
|
||||||
const jclass overlay_control_data_class =
|
const jclass overlay_control_data_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||||
state.overlay_control_data_class =
|
s_overlay_control_data_class =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||||
state.overlay_control_data_constructor =
|
s_overlay_control_data_constructor =
|
||||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||||
state.overlay_control_data_id_field =
|
s_overlay_control_data_id_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||||
state.overlay_control_data_enabled_field =
|
s_overlay_control_data_enabled_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||||
state.overlay_control_data_landscape_position_field =
|
s_overlay_control_data_landscape_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||||
state.overlay_control_data_portrait_position_field =
|
s_overlay_control_data_portrait_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||||
state.overlay_control_data_foldable_position_field =
|
s_overlay_control_data_foldable_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||||
state.overlay_control_data_individual_scale_field =
|
s_overlay_control_data_individual_scale_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||||
env->DeleteLocalRef(overlay_control_data_class);
|
env->DeleteLocalRef(overlay_control_data_class);
|
||||||
|
|
||||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||||
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||||
state.patch_constructor = env->GetMethodID(
|
s_patch_constructor = env->GetMethodID(
|
||||||
patch_class, "<init>",
|
patch_class, "<init>",
|
||||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||||
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||||
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||||
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||||
state.patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||||
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||||
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||||
env->DeleteLocalRef(patch_class);
|
env->DeleteLocalRef(patch_class);
|
||||||
|
|
||||||
const jclass double_class = env->FindClass("java/lang/Double");
|
const jclass double_class = env->FindClass("java/lang/Double");
|
||||||
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||||
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||||
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||||
env->DeleteLocalRef(double_class);
|
env->DeleteLocalRef(double_class);
|
||||||
|
|
||||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||||
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||||
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||||
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||||
env->DeleteLocalRef(int_class);
|
env->DeleteLocalRef(int_class);
|
||||||
|
|
||||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||||
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||||
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||||
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||||
env->DeleteLocalRef(boolean_class);
|
env->DeleteLocalRef(boolean_class);
|
||||||
|
|
||||||
const jclass player_input_class =
|
const jclass player_input_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||||
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||||
state.player_input_constructor = env->GetMethodID(
|
s_player_input_constructor = env->GetMethodID(
|
||||||
player_input_class, "<init>",
|
player_input_class, "<init>",
|
||||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||||
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||||
state.player_input_buttons_field =
|
s_player_input_buttons_field =
|
||||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||||
state.player_input_analogs_field =
|
s_player_input_analogs_field =
|
||||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||||
state.player_input_motions_field =
|
s_player_input_motions_field =
|
||||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||||
state.player_input_vibration_enabled_field =
|
s_player_input_vibration_enabled_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||||
state.player_input_vibration_strength_field =
|
s_player_input_vibration_strength_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||||
state.player_input_body_color_left_field =
|
s_player_input_body_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||||
state.player_input_body_color_right_field =
|
s_player_input_body_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||||
state.player_input_button_color_left_field =
|
s_player_input_button_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||||
state.player_input_button_color_right_field =
|
s_player_input_button_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||||
state.player_input_profile_name_field =
|
s_player_input_profile_name_field =
|
||||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||||
state.player_input_use_system_vibrator_field =
|
s_player_input_use_system_vibrator_field =
|
||||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||||
env->DeleteLocalRef(player_input_class);
|
env->DeleteLocalRef(player_input_class);
|
||||||
|
|
||||||
const jclass yuzu_input_device_interface =
|
const jclass yuzu_input_device_interface =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||||
state.yuzu_input_device_interface =
|
s_yuzu_input_device_interface =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||||
state.yuzu_input_device_get_name =
|
s_yuzu_input_device_get_name =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||||
state.yuzu_input_device_get_guid =
|
s_yuzu_input_device_get_guid =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||||
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||||
"()I");
|
"()I");
|
||||||
state.yuzu_input_device_get_supports_vibration =
|
s_yuzu_input_device_get_supports_vibration =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||||
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||||
"(F)V");
|
"(F)V");
|
||||||
state.yuzu_input_device_get_axes =
|
s_yuzu_input_device_get_axes =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||||
state.yuzu_input_device_has_keys =
|
s_yuzu_input_device_has_keys =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||||
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
|
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
||||||
"(ILjava/lang/String;)V");
|
"(ILjava/lang/String;)V");
|
||||||
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
|
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
||||||
|
|
||||||
// Initialize Android Storage
|
// Initialize Android Storage
|
||||||
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
|
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||||
|
|
||||||
// Initialize applets
|
// Initialize applets
|
||||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||||
|
|||||||
+18
-4
@@ -22,22 +22,36 @@ template <typename T>
|
|||||||
return std::size_t(sizeof(T) * CHAR_BIT);
|
return std::size_t(sizeof(T) * CHAR_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires std::is_integral_v<T>
|
||||||
|
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
|
||||||
|
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
||||||
return std::bit_width(value) - 1;
|
return T(MostSignificantBit<T>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
||||||
return std::bit_width(value - 1);
|
const T log2_f = Log2Floor<T>(value);
|
||||||
|
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires std::is_integral_v<T>
|
||||||
|
[[nodiscard]] T NextPow2(T value) {
|
||||||
|
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t bit_index, typename T>
|
template <size_t bit_index, typename T>
|
||||||
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr bool Bit(const T value) {
|
[[nodiscard]] constexpr bool Bit(const T value) {
|
||||||
return (T(value >> bit_index) & T(1)) == T(1);
|
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
|
||||||
|
return ((value >> bit_index) & T(1)) == T(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -25,18 +25,26 @@ template <typename T>
|
|||||||
requires std::is_unsigned_v<T>
|
requires std::is_unsigned_v<T>
|
||||||
inline std::size_t HashValue(T val) {
|
inline std::size_t HashValue(T val) {
|
||||||
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
||||||
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
const unsigned int length =
|
||||||
|
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||||
|
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
|
|
||||||
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
|
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
||||||
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
|
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
||||||
|
|
||||||
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t Bits>
|
template <size_t Bits>
|
||||||
struct HashCombineImpl {
|
struct HashCombineImpl {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T fn(T seed, T value) {
|
static inline T fn(T seed, T value) {
|
||||||
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -15,14 +12,14 @@ namespace Common {
|
|||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||||
return N((D(number) + divisor - 1) / divisor);
|
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ceiled integer division with logarithmic divisor in base 2
|
/// Ceiled integer division with logarithmic divisor in base 2
|
||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||||
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <utility>
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
struct PairHash {
|
||||||
|
template <class T1, class T2>
|
||||||
|
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
||||||
|
std::size_t seed = std::hash<T1>()(pair.first);
|
||||||
|
boost::hash_combine(seed, std::hash<T2>()(pair.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct IdentityHash {
|
||||||
|
[[nodiscard]] size_t operator()(T value) const noexcept {
|
||||||
|
return static_cast<size_t>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
+16
-10
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -19,12 +16,14 @@ namespace Common {
|
|||||||
|
|
||||||
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
||||||
if (c >= 65 && c <= 70) {
|
if (c >= 65 && c <= 70) {
|
||||||
return u8(c - 55);
|
return static_cast<u8>(c - 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c >= 97 && c <= 102) {
|
if (c >= 97 && c <= 102) {
|
||||||
return u8(c - 87);
|
return static_cast<u8>(c - 87);
|
||||||
}
|
}
|
||||||
return u8(c - 48);
|
|
||||||
|
return static_cast<u8>(c - 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||||
@@ -32,28 +31,35 @@ namespace Common {
|
|||||||
template <std::size_t Size, bool le = false>
|
template <std::size_t Size, bool le = false>
|
||||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||||
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||||
|
|
||||||
std::array<u8, Size> out{};
|
std::array<u8, Size> out{};
|
||||||
if constexpr (le) {
|
if constexpr (le) {
|
||||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||||
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
||||||
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContiguousContainer>
|
template <typename ContiguousContainer>
|
||||||
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
|
|
||||||
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||||
|
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
||||||
|
"Underlying type within the contiguous container must be u8.");
|
||||||
|
|
||||||
constexpr std::size_t pad_width = 2;
|
constexpr std::size_t pad_width = 2;
|
||||||
|
|
||||||
std::string out;
|
std::string out;
|
||||||
out.reserve(std::size(data) * pad_width);
|
out.reserve(std::size(data) * pad_width);
|
||||||
|
|
||||||
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
||||||
for (const u8 c : data)
|
for (const u8 c : data) {
|
||||||
out += fmt::format(format_str, c);
|
out += fmt::format(format_str, c);
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-10
@@ -10,7 +10,6 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
@@ -96,10 +95,9 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Level GetLevelByName(const It begin, const It end) {
|
Level GetLevelByName(const It begin, const It end) {
|
||||||
std::string_view const sv{begin, end};
|
|
||||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||||
auto const level_name = GetLevelName(Level(i));
|
const char* level_name = GetLevelName(Level(i));
|
||||||
if (boost::iequals(sv, level_name))
|
if (Common::ComparePartialString(begin, end, level_name))
|
||||||
return Level(i);
|
return Level(i);
|
||||||
}
|
}
|
||||||
return Level::Count;
|
return Level::Count;
|
||||||
@@ -107,10 +105,9 @@ Level GetLevelByName(const It begin, const It end) {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Class GetClassByName(const It begin, const It end) {
|
Class GetClassByName(const It begin, const It end) {
|
||||||
std::string_view const sv{begin, end};
|
|
||||||
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
||||||
auto const level_name = GetLogClassName(Class(i));
|
const char* level_name = GetLogClassName(Class(i));
|
||||||
if (boost::iequals(sv, level_name))
|
if (Common::ComparePartialString(begin, end, level_name))
|
||||||
return Class(i);
|
return Class(i);
|
||||||
}
|
}
|
||||||
return Class::Count;
|
return Class::Count;
|
||||||
@@ -123,13 +120,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto const sv = std::string_view{begin, level_separator};
|
const Level level = GetLevelByName(level_separator + 1, end);
|
||||||
auto const level = GetLevelByName(level_separator + 1, end);
|
|
||||||
if (level == Level::Count) {
|
if (level == Level::Count) {
|
||||||
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (boost::iequals(sv, "*")) {
|
if (Common::ComparePartialString(begin, level_separator, "*")) {
|
||||||
instance.class_levels.fill(level);
|
instance.class_levels.fill(level);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +8,6 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
@@ -18,16 +15,17 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
constexpr auto KEY_VALUE_SEPARATOR = ":";
|
constexpr char KEY_VALUE_SEPARATOR = ':';
|
||||||
constexpr auto PARAM_SEPARATOR = ",";
|
constexpr char PARAM_SEPARATOR = ',';
|
||||||
constexpr auto ESCAPE_CHARACTER = "$";
|
|
||||||
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0";
|
constexpr char ESCAPE_CHARACTER = '$';
|
||||||
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1";
|
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
|
||||||
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2";
|
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
|
||||||
|
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
|
||||||
|
|
||||||
/// A placeholder for empty param packages to avoid empty strings
|
/// A placeholder for empty param packages to avoid empty strings
|
||||||
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
||||||
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
|
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
|
||||||
|
|
||||||
ParamPackage::ParamPackage(const std::string& serialized) {
|
ParamPackage::ParamPackage(const std::string& serialized) {
|
||||||
if (serialized == EMPTY_PLACEHOLDER) {
|
if (serialized == EMPTY_PLACEHOLDER) {
|
||||||
@@ -35,20 +33,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> pairs;
|
std::vector<std::string> pairs;
|
||||||
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
|
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
|
||||||
|
|
||||||
for (const std::string& pair : pairs) {
|
for (const std::string& pair : pairs) {
|
||||||
std::vector<std::string> key_value;
|
std::vector<std::string> key_value;
|
||||||
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
|
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
|
||||||
if (key_value.size() != 2) {
|
if (key_value.size() != 2) {
|
||||||
LOG_ERROR(Common, "invalid key pair {}", pair);
|
LOG_ERROR(Common, "invalid key pair {}", pair);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& part : key_value) {
|
for (std::string& part : key_value) {
|
||||||
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR);
|
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
|
||||||
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR);
|
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
|
||||||
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER);
|
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(key_value[0], std::move(key_value[1]));
|
Set(key_value[0], std::move(key_value[1]));
|
||||||
@@ -65,10 +63,10 @@ std::string ParamPackage::Serialize() const {
|
|||||||
|
|
||||||
for (const auto& pair : data) {
|
for (const auto& pair : data) {
|
||||||
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
||||||
for (auto& part : key_value) {
|
for (std::string& part : key_value) {
|
||||||
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE);
|
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
|
||||||
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE);
|
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
|
||||||
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE);
|
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
|
||||||
}
|
}
|
||||||
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -24,6 +24,22 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
/// Make a string lowercase
|
||||||
|
std::string ToLower(const std::string_view sv) {
|
||||||
|
std::string str{sv};
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(),
|
||||||
|
[](auto const c) { return char(std::tolower(c)); });
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a string uppercase
|
||||||
|
std::string ToUpper(const std::string_view sv) {
|
||||||
|
std::string str{sv};
|
||||||
|
std::transform(str.begin(), str.end(), str.begin(),
|
||||||
|
[](auto const c) { return char(std::toupper(c)); });
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||||
std::string* _pExtension) {
|
std::string* _pExtension) {
|
||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
@@ -64,6 +80,41 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
|
||||||
|
std::istringstream iss(str);
|
||||||
|
output.resize(1);
|
||||||
|
|
||||||
|
while (std::getline(iss, *output.rbegin(), delim)) {
|
||||||
|
output.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
output.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TabsToSpaces(int tab_size, std::string in) {
|
||||||
|
std::size_t i = 0;
|
||||||
|
|
||||||
|
while ((i = in.find('\t')) != std::string::npos) {
|
||||||
|
in.replace(i, 1, tab_size, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
|
||||||
|
std::size_t pos = 0;
|
||||||
|
|
||||||
|
if (src == dest)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
while ((pos = result.find(src, pos)) != std::string::npos) {
|
||||||
|
result.replace(pos, src.size(), dest);
|
||||||
|
pos += dest.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string UTF16ToUTF8(std::u16string_view input) {
|
std::string UTF16ToUTF8(std::u16string_view input) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
return convert.to_bytes(input.data(), input.data() + input.size());
|
return convert.to_bytes(input.data(), input.data() + input.size());
|
||||||
|
|||||||
@@ -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: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
@@ -16,6 +16,12 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
/// Make a string lowercase
|
||||||
|
[[nodiscard]] std::string ToLower(const std::string_view sv);
|
||||||
|
|
||||||
|
/// Make a string uppercase
|
||||||
|
[[nodiscard]] std::string ToUpper(const std::string_view sv);
|
||||||
|
|
||||||
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
@@ -23,8 +29,37 @@ namespace Common {
|
|||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns " hej " into "hej". Also handles tabs.
|
||||||
|
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
|
||||||
|
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
||||||
|
if (str.npos != s)
|
||||||
|
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "\"hello\"" is turned to "hello"
|
||||||
|
/// This one assumes that the string has already been space stripped in both
|
||||||
|
/// ends, as done by StripSpaces above, for example.
|
||||||
|
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
|
||||||
|
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
||||||
|
return std::string{s.substr(1, s.size() - 2)};
|
||||||
|
return std::string{s};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
|
||||||
|
return value ? "True" : "False";
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
||||||
|
|
||||||
|
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||||
|
|
||||||
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||||
|
std::string* _pExtension);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
|
||||||
|
const std::string& dest);
|
||||||
|
|
||||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||||
@@ -38,13 +73,30 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
|
|
||||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||||
|
* `other` for equality.
|
||||||
|
*/
|
||||||
|
template <typename InIt>
|
||||||
|
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
|
||||||
|
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
||||||
|
if (*begin != *other) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only return true if both strings finished at the same point
|
||||||
|
return (begin == end) == (*other == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// NUL-terminated then the string ends at max_len characters.
|
/// NUL-terminated then the string ends at max_len characters.
|
||||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len);
|
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
||||||
|
std::size_t max_len);
|
||||||
|
|
||||||
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||||
/// max_len_bytes.
|
/// max_len_bytes.
|
||||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len);
|
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||||
|
std::size_t max_len);
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <dynarmic/interface/A64/a64.h>
|
#include <dynarmic/interface/A64/a64.h>
|
||||||
#include <dynarmic/interface/code_page.h>
|
#include <dynarmic/interface/code_page.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/hash.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|||||||
+19
-12
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||||
/// Initialization
|
/// Initialization
|
||||||
system.RegisterCoreThread(core);
|
system.RegisterCoreThread(core);
|
||||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||||
@@ -178,19 +178,26 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
|||||||
auto& data = core_data[core];
|
auto& data = core_data[core];
|
||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
SCOPE_EXIT {
|
||||||
|
data.host_context->Exit();
|
||||||
|
};
|
||||||
|
|
||||||
// Running
|
// Running
|
||||||
gpu_barrier->arrive_and_wait();
|
if (!gpu_barrier->Sync(token)) {
|
||||||
if (!stop_token.stop_requested()) {
|
return;
|
||||||
if (!is_async_gpu && !is_multicore) {
|
|
||||||
system.GPU().ObtainContext();
|
|
||||||
}
|
|
||||||
auto& kernel = system.Kernel();
|
|
||||||
auto& scheduler = *kernel.CurrentScheduler();
|
|
||||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
|
||||||
Kernel::SetCurrentThread(kernel, thread);
|
|
||||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
|
||||||
}
|
}
|
||||||
data.host_context->Exit();
|
|
||||||
|
if (!is_async_gpu && !is_multicore) {
|
||||||
|
system.GPU().ObtainContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
|
auto& scheduler = *kernel.CurrentScheduler();
|
||||||
|
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||||
|
Kernel::SetCurrentThread(kernel, thread);
|
||||||
|
|
||||||
|
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <barrier>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -53,7 +52,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnGpuReady() {
|
void OnGpuReady() {
|
||||||
gpu_barrier->arrive_and_wait();
|
gpu_barrier->Sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -96,7 +95,7 @@ private:
|
|||||||
|
|
||||||
static constexpr std::size_t max_cycle_runs = 5;
|
static constexpr std::size_t max_cycle_runs = 5;
|
||||||
|
|
||||||
std::optional<std::barrier<>> gpu_barrier{};
|
std::optional<Common::Barrier> gpu_barrier{};
|
||||||
struct CoreData {
|
struct CoreData {
|
||||||
std::shared_ptr<Common::Fiber> host_context;
|
std::shared_ptr<Common::Fiber> host_context;
|
||||||
std::jthread host_thread;
|
std::jthread host_thread;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
@@ -623,7 +622,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
|||||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||||
} else {
|
} else {
|
||||||
boost::algorithm::to_lower(out[0]);
|
out[0] = Common::ToLower(out[0]);
|
||||||
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
||||||
const auto& index = iter128->second;
|
const auto& index = iter128->second;
|
||||||
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
@@ -42,13 +41,16 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
|
|||||||
|
|
||||||
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
||||||
|
|
||||||
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) {
|
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
||||||
const auto upper = boost::algorithm::to_upper_copy(name);
|
const std::string& name) {
|
||||||
|
const auto upper = Common::ToUpper(name);
|
||||||
|
|
||||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||||
if (dir->GetFile(fname) != nullptr) {
|
if (dir->GetFile(fname) != nullptr) {
|
||||||
return dir->GetFile(fname);
|
return dir->GetFile(fname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
@@ -74,10 +73,12 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
|
|||||||
#else
|
#else
|
||||||
const auto subdirs = dir->GetSubdirectories();
|
const auto subdirs = dir->GetSubdirectories();
|
||||||
for (const auto& subdir : subdirs) {
|
for (const auto& subdir : subdirs) {
|
||||||
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
|
std::string dir_name = Common::ToLower(subdir->GetName());
|
||||||
|
if (dir_name == name) {
|
||||||
return subdir;
|
return subdir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
@@ -1415,10 +1414,11 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
|
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
||||||
if (ext == "nsp") {
|
|
||||||
|
if (extension == "nsp") {
|
||||||
ProcessNSP(file);
|
ProcessNSP(file);
|
||||||
} else if (ext == "xci") {
|
} else if (extension == "xci") {
|
||||||
ProcessXCI(file);
|
ProcessXCI(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
@@ -64,8 +63,8 @@ NAX::NAX(VirtualFile file_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]});
|
const std::string two_dir = Common::ToUpper(std::string{match[1]});
|
||||||
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]});
|
const std::string nca_id = Common::ToLower(std::string{match[2]});
|
||||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -22,14 +19,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
|
|||||||
|
|
||||||
// Virtual to Physical core map.
|
// Virtual to Physical core map.
|
||||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||||
0, 1, 2, 3, 0, 0, 0, 0,
|
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
||||||
|
|||||||
@@ -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 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include "common/concepts.h"
|
#include "common/concepts.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -171,7 +170,8 @@ FileType GuessFromFilename(const std::string& name) {
|
|||||||
else if (name == "00")
|
else if (name == "00")
|
||||||
return FileType::NCA;
|
return FileType::NCA;
|
||||||
|
|
||||||
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
|
auto const extension =
|
||||||
|
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||||
if (extension == "nro")
|
if (extension == "nro")
|
||||||
return FileType::NRO;
|
return FileType::NRO;
|
||||||
else if (extension == "nso")
|
else if (extension == "nso")
|
||||||
|
|||||||
+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.");
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// windows.h needs to be included before shellapi.h
|
// windows.h needs to be included before shellapi.h
|
||||||
@@ -133,7 +132,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
|
|||||||
std::string line;
|
std::string line;
|
||||||
std::getline(file, line);
|
std::getline(file, line);
|
||||||
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
|
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
|
||||||
boost::trim(line);
|
line = Common::StripSpaces(line);
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
// An empty line marks start of the IP ban list
|
// An empty line marks start of the IP ban list
|
||||||
ban_list_type = true;
|
ban_list_type = true;
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include "common/httplib.h"
|
#include "common/httplib.h"
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ void DiscordImpl::Pause() {
|
|||||||
|
|
||||||
std::string DiscordImpl::GetGameString(const std::string& title) {
|
std::string DiscordImpl::GetGameString(const std::string& title) {
|
||||||
// Convert to lowercase
|
// Convert to lowercase
|
||||||
std::string icon_name = boost::algorithm::to_lower_copy(title);
|
std::string icon_name = Common::ToLower(title);
|
||||||
|
|
||||||
// Replace spaces with dashes
|
// Replace spaces with dashes
|
||||||
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
|
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
|
||||||
|
|||||||
@@ -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))...);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||||
if (IsRegionGpuModified(device_addr, size)) {
|
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
||||||
ClearDownload(device_addr, size);
|
ClearDownload(device_addr, size);
|
||||||
gpu_modified_ranges.Subtract(device_addr, size);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
}
|
}
|
||||||
@@ -249,10 +249,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
|||||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||||
if (has_new_downloads) {
|
if (has_new_downloads) {
|
||||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||||
const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
|
||||||
if (should_sync) {
|
|
||||||
runtime.Finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||||
@@ -315,8 +311,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
|||||||
MarkWrittenBuffer(buffer_id, device_addr, size);
|
MarkWrittenBuffer(buffer_id, device_addr, size);
|
||||||
break;
|
break;
|
||||||
case ObtainBufferOperation::DiscardWrite: {
|
case ObtainBufferOperation::DiscardWrite: {
|
||||||
ClearDownload(device_addr, size);
|
const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
|
||||||
gpu_modified_ranges.Subtract(device_addr, size);
|
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
|
||||||
|
const size_t new_size = device_addr_end - device_addr_start;
|
||||||
|
ClearDownload(device_addr_start, new_size);
|
||||||
|
gpu_modified_ranges.Subtract(device_addr_start, new_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1234,7 +1233,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
|||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||||
@@ -1520,12 +1519,10 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) {
|
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
if (needs_sync) {
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
buffer.setWriteTick(runtime.CurrentTick());
|
||||||
buffer.setWriteTick(runtime.CurrentTick());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||||
gpu_modified_ranges.Add(device_addr, size);
|
gpu_modified_ranges.Add(device_addr, size);
|
||||||
@@ -1541,11 +1538,8 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
const BufferId buffer_id = page_table[page];
|
const BufferId buffer_id = page_table[page];
|
||||||
if (buffer_id) {
|
if (buffer_id) {
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
|
WaitForGpuFenceIfNeeded(buffer);
|
||||||
if (buffer.IsInBounds(device_addr, size)) {
|
if (buffer.IsInBounds(device_addr, size)) {
|
||||||
const bool should_sync = Settings::IsGPUFenceBehaviorAccurate();
|
|
||||||
if (should_sync) {
|
|
||||||
SynchronizeBufferWrites(buffer);
|
|
||||||
}
|
|
||||||
bool usable = true;
|
bool usable = true;
|
||||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
||||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
||||||
@@ -1561,11 +1555,17 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::SynchronizeBufferWrites(Buffer& buffer) {
|
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
const u64 buffer_tick = buffer.getWriteTick();
|
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||||
if (!runtime.IsFree(buffer_tick)) {
|
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||||
runtime.Wait(buffer_tick);
|
if (gpu_fence_accurate || gpu_fence_strict) {
|
||||||
|
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
||||||
|
const u64 buffer_tick = buffer.getWriteTick();
|
||||||
|
const u64 gpu_tick = runtime.KnownGpuTick();
|
||||||
|
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
||||||
|
runtime.Wait(buffer_tick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1742,24 +1742,14 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
|||||||
u64 total_size_bytes = 0;
|
u64 total_size_bytes = 0;
|
||||||
u64 largest_copy = 0;
|
u64 largest_copy = 0;
|
||||||
const DAddr buffer_start = buffer.cpu_addr_cached;
|
const DAddr buffer_start = buffer.cpu_addr_cached;
|
||||||
const auto add_upload = [&](DAddr start, DAddr end) {
|
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||||
if (start == end) return;
|
|
||||||
const u64 range_size = end - start;
|
|
||||||
upload_copies.push_back(BufferCopy{
|
upload_copies.push_back(BufferCopy{
|
||||||
.src_offset = total_size_bytes,
|
.src_offset = total_size_bytes,
|
||||||
.dst_offset = start - buffer_start,
|
.dst_offset = device_addr_out - buffer_start,
|
||||||
.size = range_size,
|
.size = range_size,
|
||||||
});
|
});
|
||||||
total_size_bytes += range_size;
|
total_size_bytes += range_size;
|
||||||
largest_copy = (std::max)(largest_copy, range_size);
|
largest_copy = (std::max)(largest_copy, range_size);
|
||||||
};
|
|
||||||
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
|
||||||
DAddr upload_start = device_addr_out;
|
|
||||||
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
|
|
||||||
add_upload(upload_start, gpu_start);
|
|
||||||
upload_start = gpu_end;
|
|
||||||
});
|
|
||||||
add_upload(upload_start, device_addr_out + range_size);
|
|
||||||
});
|
});
|
||||||
if (total_size_bytes == 0) {
|
if (total_size_bytes == 0) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1798,6 +1788,9 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
if (immediate_buffer.empty()) {
|
if (immediate_buffer.empty()) {
|
||||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||||
}
|
}
|
||||||
|
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||||
|
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||||
|
}
|
||||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||||
}
|
}
|
||||||
@@ -1816,6 +1809,9 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
for (BufferCopy& copy : copies) {
|
for (BufferCopy& copy : copies) {
|
||||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||||
|
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||||
|
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||||
|
}
|
||||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||||
// Apply the staging offset
|
// Apply the staging offset
|
||||||
copy.src_offset += upload_staging.offset;
|
copy.src_offset += upload_staging.offset;
|
||||||
|
|||||||
@@ -430,11 +430,11 @@ private:
|
|||||||
|
|
||||||
void UpdateComputeTextureBuffers();
|
void UpdateComputeTextureBuffers();
|
||||||
|
|
||||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false);
|
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
||||||
|
|
||||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
||||||
|
|
||||||
void SynchronizeBufferWrites(Buffer& buffer);
|
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||||
|
|
||||||
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
constexpr u32 MacroRegistersStart = 0xE00;
|
constexpr u32 MacroRegistersStart = 0xE00;
|
||||||
constexpr u32 ComputeInline = 0x6D;
|
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
||||||
|
|
||||||
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
||||||
: system{system_}
|
: system{system_}
|
||||||
@@ -73,16 +73,11 @@ bool DmaPusher::Step() {
|
|||||||
synced = false;
|
synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
|
||||||
|
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
|
||||||
|
}
|
||||||
|
|
||||||
if (header.size > 0) {
|
if (header.size > 0) {
|
||||||
if (subchannels[dma_state.subchannel] && dma_state.method_count) {
|
|
||||||
const auto engine = subchannel_type[dma_state.subchannel];
|
|
||||||
const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing;
|
|
||||||
const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart;
|
|
||||||
if (kepler_payload || macro_payload) {
|
|
||||||
const size_t words = std::min<size_t>(dma_state.method_count, header.size);
|
|
||||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
||||||
if (use_safe) {
|
if (use_safe) {
|
||||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
||||||
|
|||||||
@@ -49,16 +49,13 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
||||||
UploadInfo info{.upload_address = upload_address,
|
UploadInfo info{.upload_address = upload_address,
|
||||||
.exec_address = upload_state.ExecTargetAddress(),
|
.exec_address = upload_state.ExecTargetAddress(),
|
||||||
.copy_size = upload_state.GetUploadSize(),
|
.copy_size = upload_state.GetUploadSize()};
|
||||||
.was_dirty = upload_dirty};
|
|
||||||
uploads.push_back(info);
|
uploads.push_back(info);
|
||||||
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
upload_dirty = current_dirty;
|
|
||||||
current_dirty = false;
|
|
||||||
upload_state.ProcessData(method_argument, is_last_call);
|
upload_state.ProcessData(method_argument, is_last_call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -67,11 +64,9 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
|
|
||||||
for (auto& data : uploads) {
|
for (auto& data : uploads) {
|
||||||
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
||||||
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) {
|
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) &&
|
||||||
const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size);
|
memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) {
|
||||||
if (data.was_dirty || source_dirty) {
|
indirect_compute = {data.upload_address};
|
||||||
indirect_compute = {data.upload_address};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uploads.clear();
|
uploads.clear();
|
||||||
@@ -88,8 +83,6 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32*
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
upload_dirty = current_dirty;
|
|
||||||
current_dirty = false;
|
|
||||||
upload_state.ProcessData(base_start, amount);
|
upload_state.ProcessData(base_start, amount);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -226,13 +226,11 @@ private:
|
|||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
Upload::State upload_state;
|
Upload::State upload_state;
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
bool upload_dirty{};
|
|
||||||
|
|
||||||
struct UploadInfo {
|
struct UploadInfo {
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
GPUVAddr exec_address;
|
GPUVAddr exec_address;
|
||||||
u32 copy_size;
|
u32 copy_size;
|
||||||
bool was_dirty;
|
|
||||||
};
|
};
|
||||||
std::vector<UploadInfo> uploads;
|
std::vector<UploadInfo> uploads;
|
||||||
std::optional<GPUVAddr> indirect_compute{};
|
std::optional<GPUVAddr> indirect_compute{};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalFence(std::function<void()>&& func) {
|
void SignalFence(std::function<void()>&& func) {
|
||||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict();
|
||||||
const bool should_flush = ShouldFlush();
|
const bool should_flush = ShouldFlush();
|
||||||
if constexpr (!can_async_check) {
|
if constexpr (!can_async_check) {
|
||||||
TryReleasePendingFences<false>();
|
TryReleasePendingFences<false>();
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
|||||||
};
|
};
|
||||||
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
||||||
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
||||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence;
|
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence;
|
||||||
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
||||||
pointer, pointer_timestamp] {
|
pointer, pointer_timestamp] {
|
||||||
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
||||||
|
|||||||
@@ -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 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -55,7 +55,7 @@ size_t StagingBuffers::RequestBuffer(size_t requested_size) {
|
|||||||
}
|
}
|
||||||
StagingBufferAlloc alloc;
|
StagingBufferAlloc alloc;
|
||||||
alloc.buffer.Create();
|
alloc.buffer.Create();
|
||||||
const auto next_pow2_size = std::bit_ceil(requested_size);
|
const auto next_pow2_size = Common::NextPow2(requested_size);
|
||||||
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
|
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
|
||||||
storage_flags | GL_MAP_PERSISTENT_BIT);
|
storage_flags | GL_MAP_PERSISTENT_BIT);
|
||||||
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
|
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
|
||||||
|
|||||||
@@ -1404,7 +1404,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
|
|||||||
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
|
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
|
||||||
if (pbo_size < copy_size) {
|
if (pbo_size < copy_size) {
|
||||||
intermediate_pbo.Create();
|
intermediate_pbo.Create();
|
||||||
pbo_size = std::bit_ceil(copy_size);
|
pbo_size = Common::NextPow2(copy_size);
|
||||||
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
|
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
|
||||||
}
|
}
|
||||||
// Copy from source to PBO
|
// Copy from source to PBO
|
||||||
|
|||||||
@@ -94,14 +94,11 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
||||||
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
||||||
const bool use_accelerated = texture_info.has_value();
|
const bool use_accelerated = texture_info.has_value();
|
||||||
const bool is_applet =
|
|
||||||
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
|
|
||||||
Service::Nvnflinger::LayerStackId::Recording)) == 0;
|
|
||||||
|
|
||||||
RefreshResources(device, framebuffer);
|
RefreshResources(device, framebuffer);
|
||||||
SetAntiAliasPass(device);
|
SetAntiAliasPass(device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
SetPostProcessPass(device, is_applet);
|
SetPostProcessPass(device);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
@@ -141,11 +138,8 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||||
crop_rect = {0, 0, 1, 1};
|
crop_rect = {0, 0, 1, 1};
|
||||||
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
||||||
if (!is_applet) {
|
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||||
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image,
|
crop_rect = {0, 0, 1, 1};
|
||||||
source_image_view, render_extent, crop_rect);
|
|
||||||
crop_rect = {0, 0, 1, 1};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetMatrixData(device, *out_push_constants, layout);
|
SetMatrixData(device, *out_push_constants, layout);
|
||||||
@@ -234,11 +228,7 @@ void Layer::SetAntiAliasPass(const Device& device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void Layer::SetPostProcessPass(const Device& device, bool is_applet) {
|
void Layer::SetPostProcessPass(const Device& device) {
|
||||||
if (is_applet) {
|
|
||||||
post_process.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VkExtent2D render_area{
|
const VkExtent2D render_area{
|
||||||
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
||||||
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ private:
|
|||||||
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
||||||
void SetAntiAliasPass(const Device& device);
|
void SetAntiAliasPass(const Device& device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void SetPostProcessPass(const Device& device, bool is_applet);
|
void SetPostProcessPass(const Device& device);
|
||||||
#endif
|
#endif
|
||||||
void ReleaseRawImages();
|
void ReleaseRawImages();
|
||||||
|
|
||||||
|
|||||||
@@ -419,15 +419,15 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 BufferCacheRuntime::CurrentTick() {
|
u64 BufferCacheRuntime::CurrentTick() {
|
||||||
return scheduler.CurrentTick();
|
return scheduler.GetMasterSemaphore().CurrentTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCacheRuntime::IsFree(u64 tick) {
|
u64 BufferCacheRuntime::KnownGpuTick() {
|
||||||
return scheduler.IsFree(tick);
|
return scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Wait(u64 tick) {
|
void BufferCacheRuntime::Wait(u64 buffer_tick) {
|
||||||
scheduler.Wait(tick);
|
scheduler.Wait(buffer_tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Finish() {
|
void BufferCacheRuntime::Finish() {
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ public:
|
|||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
bool IsFree(u64 tick);
|
u64 KnownGpuTick();
|
||||||
|
|
||||||
void Wait(u64 tick);
|
void Wait(u64 buffer_tick);
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user