Compare commits

..

12 Commits

Author SHA1 Message Date
MaranBr 67bada77f8 [video_core] Skip fragment shaders with known issues on non-NVIDIA drivers (#4503)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Skip fragment shaders using a pattern that is not supported by non-NVIDIA drivers.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4503
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-10-08 23:11:34 +02:00
PavelBARABANOV 3cdd2ce141 [android, renderer_vulkan] Сhange default settings (#4509)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Airplane mode: On - needed to launch many games.
Nvdec: CPU - fixes most issues in cutscenes.
AudioEngine: Sdl3 (only Android) - "Auto" includes Cubeb on Android, but SDL3 works better.
Async GPU: On - In most games, this provides a 30% boost in performance.
Async presentation: On (Remove async presentation toggle from Android, It is no longer needed).

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4509
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-10-08 19:02:48 +02:00
xbzk 10bcd2d849 [settings] add PR4490 & PR4492 toggles (#4502)
- [X] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [X] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [X] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
TEMPORARY! Pushed under Camille's instruction. Will be dropped as soon as they get optimized.
Aimed to avoid reported perf impact from PRs 4490 and 4492 on 30-sep-2026 nightly,

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4502
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-10-05 22:37:30 +02:00
Eden CI 48aeedd3b3 [dist, android] Update translations from Transifex for Oct 05 (#4507)
Automatic translation update for Oct 05

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4507
2026-10-05 21:18:09 +02:00
MaranBr d16735f5b6 [am] Properly handle None frontend applet (#4500)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Properly handle the `AppletId::None` case by returning a null frontend applet instead of falling back to the stub implementation.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4500
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-10-02 02:24:58 +02:00
lizzie d3550c4571 [qt] Filter collates accents (Unicode NFD) (#4380)
Now "POKEMON" and "pokemon" are the same, and search's no longer case sensitive.
Maybe we could transliterate? But that would require icu which is a bit overkill.
Main point is to be able to search "Pokemon" (accent on 'e') without actually typing the accent

https://unicode.org/reports/tr15/

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

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

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4380
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-10-01 07:39:44 +02:00
John 963119e7cc [ffmpeg]Gamescope / Gamemode NVDEC Emulation - GPU Decoding Patches (#4501)
Switch to ffmpeg with liz's gamescope patches.

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

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4501
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-10-01 03:34:57 +02:00
xbzk 2a7e9b53b1 [android,ui] fixed shader cache clearing when changing drivers (#4496)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Trivial bug pending on backlog, where changing drivers was not clearing up sharder cache as expected.

The driver flow (broken) was deleting externalFilesDir/shader/<game>, while the visible cache-size control (working) DirectoryInitialization.userDirectory/cache/shader/<game>.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4496
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-10-01 00:28:18 +02:00
xbzk 8e2d26c272 [video_core] buffer cache readback for nce invalidations (#4490)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

This is a follow up of 4473, a fix for the next Ender Magnolia known crash, the shackled beast's vaper crash, but specific for NCE.

Imma try to be short:

NCE invalidation was dooming whole guest pages when it shouldn't. If the GPU buffer cache owns some bytes on a page, invalidating first discards their ownership, and the retrying CPU can then observe stale data. On Android, this was corrupting some indirect draw packets and causing huge invalid index count reach staging memory allocation.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4490
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-30 22:13:37 +02:00
Eden CI f8b7651104 [dist, android] Update translations from Transifex for Sep 28 (#4494)
Automatic translation update for Sep 28

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4494
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-30 02:00:44 +02:00
xbzk 3f78512c83 [ro] add TLS patching for runtime NROs (#4492)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Aimed to fix Biomutant which stucks in a bootloop due to guest being unable to reach correct thread local storage (TLS)
This fixes runtime NRO loading by properly patching the guest TLS (TPIDR_EL0) path. Steps:
-Read the loaded NRO image into a temporary buffer.
-Run the NCE patcher on it.
-Produce a patch section / relocated code.
-Map that patch section as executable memory in the target process.
Game still has intermittent visual issues which can be solved with GPU level accurate, but that causes a huge performance drop in exchange. Someone else will resume this one.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4492
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-30 00:58:29 +02:00
xbzk a82f31a310 [settings,qt] merge of "disable controller applet" toggle with "controller applet mode" picker (#4498)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Small good simplification to nuke a toggle which costed me almost an entire day of work when fixing controller applet issues:

There were two settings related to Controller applet:
-"disable controller applet", which is a boolean toggle;
-"controller configuration" applet mode, which is a "Real"/"Custom" picker;

All i did was to nuke the toggle and add a third option "Disabled" to the picker.
Users which used the disable toggle will have to pick Disabled;
Real/Custom settings wont be affected since their indexes remains.
All options tested against diablo 3. All working as intended.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4498
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-30 00:57:23 +02:00
104 changed files with 12926 additions and 13764 deletions
+1 -1
View File
@@ -79,7 +79,7 @@
"name": "ffmpeg", "name": "ffmpeg",
"package": "FFmpeg", "package": "FFmpeg",
"repo": "crueter-ci/FFmpeg", "repo": "crueter-ci/FFmpeg",
"version": "9.0.1-1788120736-bf1b838f2a" "version": "9.0.2-1790726865-946fcce07b"
}, },
"fmt": { "fmt": {
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2", "hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
+547 -576
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+443 -477
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -470
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -470
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -470
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+443 -477
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -470
View File
File diff suppressed because it is too large Load Diff
+442 -470
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+443 -477
View File
File diff suppressed because it is too large Load Diff
+444 -474
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+447 -474
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
+443 -477
View File
File diff suppressed because it is too large Load Diff
+442 -469
View File
File diff suppressed because it is too large Load Diff
@@ -16,6 +16,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_USE_SPEED_LIMIT("use_speed_limit"), RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"), USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
NCE_INVALIDATION_GPU_READBACK("nce_invalidation_gpu_readback"),
NCE_RUNTIME_NRO_PATCH("nce_runtime_nro_patch"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"), FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"), EMULATE_BGR565("emulate_bgr565"),
RESCALE_HACK("rescale_hack"), RESCALE_HACK("rescale_hack"),
@@ -25,7 +27,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"), RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
RENDERER_FORCE_MAX_CLOCK("force_max_clock"), RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"), RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"), ENABLE_BUFFER_HISTORY("enable_buffer_history"),
@@ -753,13 +753,6 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_gpu_emulation_description descriptionId = R.string.renderer_asynchronous_gpu_emulation_description
) )
) )
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNC_PRESENTATION,
titleId = R.string.renderer_async_presentation,
descriptionId = R.string.renderer_async_presentation_description
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.DMA_ACCURACY, IntSetting.DMA_ACCURACY,
@@ -866,6 +859,20 @@ abstract class SettingsItem(
descriptionId = R.string.skip_cpu_inner_invalidation_description descriptionId = R.string.skip_cpu_inner_invalidation_description
) )
) )
put(
SwitchSetting(
BooleanSetting.NCE_INVALIDATION_GPU_READBACK,
titleId = R.string.nce_invalidation_gpu_readback,
descriptionId = R.string.nce_invalidation_gpu_readback_description
)
)
put(
SwitchSetting(
BooleanSetting.NCE_RUNTIME_NRO_PATCH,
titleId = R.string.nce_runtime_nro_patch,
descriptionId = R.string.nce_runtime_nro_patch_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.FIX_BLOOM_EFFECTS, BooleanSetting.FIX_BLOOM_EFFECTS,
@@ -554,12 +554,13 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.hacks)) add(HeaderSetting(R.string.hacks))
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.NCE_INVALIDATION_GPU_READBACK.key)
add(BooleanSetting.NCE_RUNTIME_NRO_PATCH.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key) add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key) add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
add(SettingsItem.GPU_UNSWIZZLE_COMBINED) add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
add(HeaderSetting(R.string.extensions)) add(HeaderSetting(R.string.extensions))
@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File import java.io.File
@@ -146,11 +147,11 @@ class DriverViewModel : ViewModel() {
private fun wipeGameShaders(game: Game) { private fun wipeGameShaders(game: Game) {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val externalFilesDir = YuzuApplication.appContext.getExternalFilesDir(null) val userDirectory = DirectoryInitialization.userDirectory
?: return@withContext ?: return@withContext
val shaderDir = File( val shaderDir = File(
externalFilesDir.absolutePath + userDirectory +
"/shader/" + game.settingsName.lowercase() "/cache/shader/" + game.settingsName.lowercase()
) )
if (shaderDir.exists()) { if (shaderDir.exists()) {
shaderDir.deleteRecursively() shaderDir.deleteRecursively()
@@ -28,7 +28,7 @@
<string name="input_overlay_behavior">السلوك</string> <string name="input_overlay_behavior">السلوك</string>
<string name="overlay_auto_hide">إخفاء الطبقة تلقائيًا</string> <string name="overlay_auto_hide">إخفاء الطبقة تلقائيًا</string>
<string name="overlay_auto_hide_description">إخفاء طبقة ذراع التحكم باللمس تلقائيًا بعد مرور الوقت المحدد من عدم النشاط.</string> <string name="overlay_auto_hide_description">إخفاء طبقة ذراع التحكم باللمس تلقائيًا بعد مرور الوقت المحدد من عدم النشاط.</string>
<string name="enable_input_overlay_auto_hide">تمكين إخفاء الطبقة تلقائيًا</string> <string name="enable_input_overlay_auto_hide">تفعيل إخفاء الطبقة تلقائيًا</string>
<string name="hide_overlay_on_controller_input">إخفاء الطبقة عند إدخال ذراع التحكم</string> <string name="hide_overlay_on_controller_input">إخفاء الطبقة عند إدخال ذراع التحكم</string>
<string name="hide_overlay_on_controller_input_description">إخفاء تلقائي لطبقة عناصر التحكم باللمس عند استخدام ذراع تحكم فعلية. تظهر الطبقة مرة أخرى عند فصل ذراع التحكم.</string> <string name="hide_overlay_on_controller_input_description">إخفاء تلقائي لطبقة عناصر التحكم باللمس عند استخدام ذراع تحكم فعلية. تظهر الطبقة مرة أخرى عند فصل ذراع التحكم.</string>
<string name="invert_confirm_back_controller_buttons">عكس أزرار التأكيد/الرجوع في وحدة التحكم</string> <string name="invert_confirm_back_controller_buttons">عكس أزرار التأكيد/الرجوع في وحدة التحكم</string>
@@ -51,7 +51,7 @@
<string name="stats_overlay_customization">التخصيص</string> <string name="stats_overlay_customization">التخصيص</string>
<string name="stats_overlay_items">الرؤية</string> <string name="stats_overlay_items">الرؤية</string>
<string name="stats_overlay_options">طبقة الأداء</string> <string name="stats_overlay_options">طبقة الأداء</string>
<string name="enable_stats_overlay_">تمكين طبقة إحصائيات الأداء</string> <string name="enable_stats_overlay_">تفعيل طبقة إحصائيات الأداء</string>
<string name="stats_overlay_options_description">ضبط المعلومات التي يتم عرضها في طبقة إحصائيات الأداء</string> <string name="stats_overlay_options_description">ضبط المعلومات التي يتم عرضها في طبقة إحصائيات الأداء</string>
<string name="show_fps">عرض معدل الإطارات</string> <string name="show_fps">عرض معدل الإطارات</string>
<string name="show_fps_description">عرض معدل الإطارات الحالي</string> <string name="show_fps_description">عرض معدل الإطارات الحالي</string>
@@ -81,7 +81,7 @@
<!-- Device Overlay settings --> <!-- Device Overlay settings -->
<string name="show_soc_overlay">عرض طبقة معلومات الجهاز</string> <string name="show_soc_overlay">عرض طبقة معلومات الجهاز</string>
<string name="enable_soc_overlay">تمكين طبقة الجهاز</string> <string name="enable_soc_overlay">تفعيل طبقة الجهاز</string>
<string name="soc_overlay_options">طبقة الجهاز</string> <string name="soc_overlay_options">طبقة الجهاز</string>
<string name="soc_overlay_options_description">ضبط المعلومات التي يتم عرضها في طبقة الجهاز</string> <string name="soc_overlay_options_description">ضبط المعلومات التي يتم عرضها في طبقة الجهاز</string>
@@ -98,8 +98,8 @@
<string name="use_sync_core">مزامنة سرعة النواة</string> <string name="use_sync_core">مزامنة سرعة النواة</string>
<string name="use_sync_core_description">قم بمزامنة سرعة النواة مع النسبة المئوية للسرعة القصوى لتحسين الأداء دون تغيير السرعة الفعلية للعبة.</string> <string name="use_sync_core_description">قم بمزامنة سرعة النواة مع النسبة المئوية للسرعة القصوى لتحسين الأداء دون تغيير السرعة الفعلية للعبة.</string>
<string name="cpuopt_unsafe_host_mmu">تمكين محاكاة MMU المضيف</string> <string name="cpuopt_unsafe_host_mmu">تفعيل محاكاة MMU المضيف</string>
<string name="cpuopt_unsafe_host_mmu_description">يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</string> <string name="cpuopt_unsafe_host_mmu_description">يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تفعيله إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</string>
<string name="debug_knobs">مقابض تصحيح الأخطاء</string> <string name="debug_knobs">مقابض تصحيح الأخطاء</string>
<string name="debug_knobs_description">للإستخدام في التطوير فقط.</string> <string name="debug_knobs_description">للإستخدام في التطوير فقط.</string>
<string name="debug_knobs_hint">0 إلى 65535</string> <string name="debug_knobs_hint">0 إلى 65535</string>
@@ -342,7 +342,7 @@
<string name="frame_gen_flow_scale_description">دقة مسار التدفق البصري، كجزء من الناتج. ويُعد خفض هذه القيمة أرخص طريقة لاستعادة الأداء.</string> <string name="frame_gen_flow_scale_description">دقة مسار التدفق البصري، كجزء من الناتج. ويُعد خفض هذه القيمة أرخص طريقة لاستعادة الأداء.</string>
<string name="frame_gen_unsupported">توليد الإطار غير متاح</string> <string name="frame_gen_unsupported">توليد الإطار غير متاح</string>
<string name="frame_gen_unsupported_description">يفتقر برنامج تشغيل وحدة معالجة الرسومات هذا إلى نموذج ذاكرة Vulkan أو دعم الدقة النصفية (float16) التي تتطلبها برامج التظليل الخاصة بـ «Lossless Scaling».</string> <string name="frame_gen_unsupported_description">يفتقر برنامج تشغيل وحدة معالجة الرسومات هذا إلى نموذج ذاكرة Vulkan أو دعم الدقة النصفية (float16) التي تتطلبها برامج التظليل الخاصة بـ «Lossless Scaling».</string>
<string name="lossless_scaling_setup_description">اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتمكين إنشاء الإطارات لاحقًا</string> <string name="lossless_scaling_setup_description">اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتفعيل إنشاء الإطارات لاحقًا</string>
<string name="lossless_scaling_install">تثبيت ملف Lossless.dll</string> <string name="lossless_scaling_install">تثبيت ملف Lossless.dll</string>
<string name="lossless_scaling_install_description">يتطلب إنشاء الإطارات أن تكون لديك نسخة قانونية خاصة بك من ملف Lossless.dll المأخوذ من برنامج Lossless Scaling</string> <string name="lossless_scaling_install_description">يتطلب إنشاء الإطارات أن تكون لديك نسخة قانونية خاصة بك من ملف Lossless.dll المأخوذ من برنامج Lossless Scaling</string>
<string name="lossless_scaling_replace_description">اختر نسخة أخرى من ملف Lossless.dll</string> <string name="lossless_scaling_replace_description">اختر نسخة أخرى من ملف Lossless.dll</string>
@@ -350,7 +350,7 @@
<string name="frame_generation_supported">مدعوم</string> <string name="frame_generation_supported">مدعوم</string>
<string name="frame_generation_unsupported">غير مدعوم (لا يتوفر نموذج ذاكرة Vulkan أو float16)</string> <string name="frame_generation_unsupported">غير مدعوم (لا يتوفر نموذج ذاكرة Vulkan أو float16)</string>
<string name="lossless_scaling">Lossless Scaling</string> <string name="lossless_scaling">Lossless Scaling</string>
<string name="lossless_scaling_description">قم بتوفير نسختك الخاصة من ملف Lossless.dll لتمكين توليد الإطارات</string> <string name="lossless_scaling_description">قم بتوفير نسختك الخاصة من ملف Lossless.dll لتفعيل توليد الإطارات</string>
<string name="lossless_scaling_installed">مثبت</string> <string name="lossless_scaling_installed">مثبت</string>
<string name="lossless_scaling_not_installed">غير مثبت</string> <string name="lossless_scaling_not_installed">غير مثبت</string>
<string name="lossless_scaling_replace">استبدل</string> <string name="lossless_scaling_replace">استبدل</string>
@@ -502,9 +502,9 @@
<string name="frame_limit_slider">نسبة الحد الأقصى للسرعة</string> <string name="frame_limit_slider">نسبة الحد الأقصى للسرعة</string>
<string name="frame_limit_slider_description">يحدد النسبة المئوية للحد من سرعة المحاكاة. %100 هي السرعة الطبيعية. القيم الأعلى أو الأقل ستزيد أو تقلل من الحد الأقصى للسرعة.</string> <string name="frame_limit_slider_description">يحدد النسبة المئوية للحد من سرعة المحاكاة. %100 هي السرعة الطبيعية. القيم الأعلى أو الأقل ستزيد أو تقلل من الحد الأقصى للسرعة.</string>
<string name="turbo_speed_limit">سرعة تيربو</string> <string name="turbo_speed_limit">سرعة تيربو</string>
<string name="turbo_speed_limit_description">عند تمكين وضع التيربو، سيتم تشغيل المحاكاة بهذه السرعة.</string> <string name="turbo_speed_limit_description">عند تفعيل وضع التيربو، سيتم تشغيل المحاكاة بهذه السرعة.</string>
<string name="slow_speed_limit">سرعة بطيئة</string> <string name="slow_speed_limit">سرعة بطيئة</string>
<string name="slow_speed_limit_description">عند تمكين الوضع البطيء، سيتم تشغيل المحاكاة بهذه السرعة.</string> <string name="slow_speed_limit_description">عند تفعيل الوضع البطيء، سيتم تشغيل المحاكاة بهذه السرعة.</string>
<string name="cpu_backend">خلفية وحدة المعالجة المركزية</string> <string name="cpu_backend">خلفية وحدة المعالجة المركزية</string>
<string name="cpu_accuracy">دقة وحدة المعالجة المركزية</string> <string name="cpu_accuracy">دقة وحدة المعالجة المركزية</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@@ -574,11 +574,9 @@
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string> <string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string> <string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string> <string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
<string name="renderer_async_presentation">عرض غير متزامن</string>
<string name="renderer_async_presentation_description">يمكن لهذه الحيلة أن تزيد من الأداء عن طريق نقل عملية العرض إلى خيط معالجة منفصل على حساب مشاكل الرسوميات.</string>
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string> <string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
<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="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>
@@ -616,7 +614,7 @@
<string name="dyna_state_description">يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في ExtendedDynamicState (EDS). كلما زادت القيمة، قلّ عدد عمليات تجميع خط الأنابيب بناءً على الحالة الديناميكية التي يدعمها برنامج التشغيل.</string> <string name="dyna_state_description">يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في ExtendedDynamicState (EDS). كلما زادت القيمة، قلّ عدد عمليات تجميع خط الأنابيب بناءً على الحالة الديناميكية التي يدعمها برنامج التشغيل.</string>
<string name="disabled">معطل</string> <string name="disabled">معطل</string>
<string name="vertex_input_dynamic_state">حالة ديناميكية لإدخال الرأس</string> <string name="vertex_input_dynamic_state">حالة ديناميكية لإدخال الرأس</string>
<string name="vertex_input_dynamic_state_description">يتيح تمكين هذه الميزة معالجة أكثر مرونة لمدخلات الرؤوس، مما قد يقلل من وقت تجميع خط الأنابيب في vertex/buffer.</string> <string name="vertex_input_dynamic_state_description">يتيح تفعيل هذه الميزة معالجة أكثر مرونة لمدخلات الرؤوس، مما قد يقلل من وقت تجميع خط الأنابيب في vertex/buffer.</string>
<string name="sample_shading_fraction">تظليل العينة</string> <string name="sample_shading_fraction">تظليل العينة</string>
<string name="sample_shading_fraction_description">يسمح هذا الخيار بتنفيذ مُظلل الأجزاء لكل عينة في جزء متعدد العينات بدلاً من تنفيذه مرة واحدة لكل جزء. يُحسّن هذا من جودة الرسومات على حساب بعض الأداء.</string> <string name="sample_shading_fraction_description">يسمح هذا الخيار بتنفيذ مُظلل الأجزاء لكل عينة في جزء متعدد العينات بدلاً من تنفيذه مرة واحدة لكل جزء. يُحسّن هذا من جودة الرسومات على حساب بعض الأداء.</string>
@@ -825,9 +823,9 @@
<string name="preferences_system">النظام</string> <string name="preferences_system">النظام</string>
<string name="preferences_system_description">وضع الإرساء، المنطقة، اللغة</string> <string name="preferences_system_description">وضع الإرساء، المنطقة، اللغة</string>
<string name="preferences_graphics">الرسومات</string> <string name="preferences_graphics">الرسومات</string>
<string name="preferences_graphics_description">مستوى الدقة، الدقة، ذاكرة التخزين المؤقتة للتظليل</string> <string name="preferences_graphics_description">مستوى دقة المحاكاة، الدقة، ذاكرة التخزين المؤقتة للتظليل</string>
<string name="quick_settings">الإعدادات السريعة</string> <string name="quick_settings">الإعدادات السريعة</string>
<string name="enable_quick_settings">تمكين الإعدادات السريعة</string> <string name="enable_quick_settings">تفعيل الإعدادات السريعة</string>
<string name="enable_quick_settings_description">السماح بالوصول إلى قائمة الإعدادات السريعة عن طريق التمرير و زر القائمة</string> <string name="enable_quick_settings_description">السماح بالوصول إلى قائمة الإعدادات السريعة عن طريق التمرير و زر القائمة</string>
<string name="preferences_audio">الصوت</string> <string name="preferences_audio">الصوت</string>
<string name="preferences_audio_description">محرك الإخراج، مستوى الصوت</string> <string name="preferences_audio_description">محرك الإخراج، مستوى الصوت</string>
@@ -1223,8 +1221,8 @@
<string name="airplane_mode">وضع الطيران</string> <string name="airplane_mode">وضع الطيران</string>
<string name="airplane_mode_description">يمرر وضع الطيران إلى نظام تشغيل Switch</string> <string name="airplane_mode_description">يمرر وضع الطيران إلى نظام تشغيل Switch</string>
<string name="enable_overlay">تمكين التطبيق الصغير للطبقة</string> <string name="enable_overlay">تفعيل التطبيق الصغير للطبقة</string>
<string name="enable_overlay_description">تمكين التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره.</string> <string name="enable_overlay_description">تفعيل التطبيق الصغير المدمج في Horizon. اضغط مع الاستمرار على زر الشاشة الرئيسية لمدة 1 ثانية لإظهاره.</string>
<!-- Profile Management --> <!-- Profile Management -->
<string name="profile_manager">إدارة ملف التعريف</string> <string name="profile_manager">إدارة ملف التعريف</string>
@@ -518,8 +518,6 @@
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string> <string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string> <string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string> <string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
<string name="renderer_async_presentation">Presentación asíncrona</string>
<string name="renderer_async_presentation_description">Este hack puede aumentar el rendimiento al mover la presentación a un hilo independiente de la CPU a costa de problemas gráficos.</string>
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string> <string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
<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>
@@ -563,8 +563,6 @@
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string> <string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string> <string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string> <string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
<string name="renderer_async_presentation">Асинхронная презентация</string>
<string name="renderer_async_presentation_description">Может повысить производительность за счёт перемещения вывода кадров в отдельный поток ЦП, но ценой возникновения графических проблем.</string>
<string name="renderer_reactive_flushing">Реактивная очистка</string> <string name="renderer_reactive_flushing">Реактивная очистка</string>
<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>
@@ -481,8 +481,6 @@
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string> <string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string> <string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string> <string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
<string name="renderer_async_presentation">Асинхронне подання</string>
<string name="renderer_async_presentation_description">Це обхідне рішення може покращити продуктивність завдяки переміщенню подання на окремий потік ЦП, але спричинить проблеми з графікою.</string>
<string name="renderer_reactive_flushing">Реактивне очищення</string> <string name="renderer_reactive_flushing">Реактивне очищення</string>
<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>
@@ -564,8 +564,6 @@
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string> <string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string> <string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string> <string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
<string name="renderer_async_presentation">异步呈现</string>
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。</string>
<string name="renderer_reactive_flushing">启用反应性刷新</string> <string name="renderer_reactive_flushing">启用反应性刷新</string>
<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>
@@ -16,7 +16,7 @@
<string name="value_too_high">範圍最大必須為%1$d</string> <string name="value_too_high">範圍最大必須為%1$d</string>
<string name="invalid_value">無效的範圍</string> <string name="invalid_value">無效的範圍</string>
<string name="using_per_game_config">使用個別設定中</string> <string name="using_per_game_config">使用個別遊戲設定中</string>
<!-- Input Overlay --> <!-- Input Overlay -->
<string name="show_input_overlay">顯示虛擬按鍵</string> <string name="show_input_overlay">顯示虛擬按鍵</string>
@@ -99,7 +99,7 @@
<string name="use_sync_core">同步核心速度</string> <string name="use_sync_core">同步核心速度</string>
<string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能</string> <string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能</string>
<string name="cpuopt_unsafe_host_mmu">啟用主機MMU模擬</string> <string name="cpuopt_unsafe_host_mmu">啟用主機MMU模擬</string>
<string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速客體程式的記憶體存取。啟用後,客體記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。</string> <string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速客體程式的記憶體存取。啟用後,客體記憶體讀取/寫入將直接在記憶體中執行並利用主機的MMU。停用此功能將強制所有記憶體存取使用軟體MMU模擬</string>
<string name="debug_knobs">偵錯開關</string> <string name="debug_knobs">偵錯開關</string>
<string name="debug_knobs_description">僅限開發用途</string> <string name="debug_knobs_description">僅限開發用途</string>
<string name="debug_knobs_hint">0到65535</string> <string name="debug_knobs_hint">0到65535</string>
@@ -151,7 +151,7 @@
<string name="multiplayer_member_leave">%1$s 離開</string> <string name="multiplayer_member_leave">%1$s 離開</string>
<string name="multiplayer_member_kicked">%1$s 被踢出</string> <string name="multiplayer_member_kicked">%1$s 被踢出</string>
<string name="multiplayer_member_banned">%1$s 被封鎖</string> <string name="multiplayer_member_banned">%1$s 被封鎖</string>
<string name="multiplayer_address_unbanned">位址已解封</string> <string name="multiplayer_address_unbanned">IP位址已解封</string>
<string name="multiplayer_kick_member">踢出成員</string> <string name="multiplayer_kick_member">踢出成員</string>
<string name="multiplayer_password">密碼</string> <string name="multiplayer_password">密碼</string>
<string name="multiplayer_joining">正在加入…</string> <string name="multiplayer_joining">正在加入…</string>
@@ -173,7 +173,7 @@
<string name="multiplayer_ban">封鎖使用者</string> <string name="multiplayer_ban">封鎖使用者</string>
<string name="multiplayer_room_browser">公開房間</string> <string name="multiplayer_room_browser">公開房間</string>
<string name="multiplayer_no_rooms_found">未找到公開房間</string> <string name="multiplayer_no_rooms_found">未找到公開房間</string>
<string name="multiplayer_password_required">需要輸入密碼</string> <string name="multiplayer_password_required">請輸入密碼</string>
<string name="multiplayer_player_count">:%1$d/%2$d</string> <string name="multiplayer_player_count">:%1$d/%2$d</string>
<string name="multiplayer_game">遊戲</string> <string name="multiplayer_game">遊戲</string>
<string name="multiplayer_no_game_info">任意遊戲</string> <string name="multiplayer_no_game_info">任意遊戲</string>
@@ -189,7 +189,7 @@
<string name="multiplayer_token_required">需要Web令牌,請前往進階設定 -> 系統 -> 網路</string> <string name="multiplayer_token_required">需要Web令牌,請前往進階設定 -> 系統 -> 網路</string>
<string name="multiplayer_ip_error">IP格式無效</string> <string name="multiplayer_ip_error">IP格式無效</string>
<string name="multiplayer_username_error">必須介於4-20個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string> <string name="multiplayer_username_error">必須介於4-20個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
<string name="multiplayer_nickname_invalid">使用者名稱無效,請在系統→網路中檢查設定</string> <string name="multiplayer_nickname_invalid">使用者名稱無效,請在系統 -&gt; 網路中檢查設定</string>
<string name="multiplayer_token_error">必須為48個字元,且僅包含小寫字母a-z</string> <string name="multiplayer_token_error">必須為48個字元,且僅包含小寫字母a-z</string>
<string name="multiplayer_port_error">埠號需為1-65535</string> <string name="multiplayer_port_error">埠號需為1-65535</string>
<string name="cancel">取消</string> <string name="cancel">取消</string>
@@ -201,16 +201,16 @@
<!-- Setup strings --> <!-- Setup strings -->
<string name="welcome">歡迎!</string> <string name="welcome">歡迎!</string>
<string name="welcome_description">瞭解如何設定 <b>Eden</b> 並開始模擬。</string> <string name="welcome_description">瞭解如何設定 <b>Eden</b> 並開始模擬</string>
<string name="get_started">開始使用</string> <string name="get_started">開始使用</string>
<string name="keys">金鑰</string> <string name="keys">金鑰</string>
<string name="keys_description">使用下方的按鈕安裝您的 <b>prod.keys</b> 檔案。</string> <string name="keys_description">使用下方的按鈕安裝您的 <b>prod.keys</b> 檔案</string>
<string name="firmware">韌體</string> <string name="firmware">韌體</string>
<string name="firmware_description">使用下方的按鈕安裝您的<b>firmware.zip</b>檔案</string> <string name="firmware_description">使用下方的按鈕安裝您的<b>firmware.zip</b>檔案</string>
<string name="games">遊戲資料夾</string> <string name="games">遊戲資料夾</string>
<string name="games_description">使用下方的按鈕選擇您的<b>遊戲</b>資料夾。</string> <string name="games_description">使用下方的按鈕選擇您的<b>遊戲</b>資料夾。</string>
<string name="done">完成</string> <string name="done">完成</string>
<string name="done_description">您已準備就緒。\n盡情遊玩您的遊戲!</string> <string name="done_description">您已準備就緒\n盡情遊玩您的遊戲!</string>
<string name="next">下一步</string> <string name="next">下一步</string>
<string name="back">上一步</string> <string name="back">上一步</string>
<string name="add_games">新增遊戲</string> <string name="add_games">新增遊戲</string>
@@ -237,7 +237,7 @@
<string name="update_install_failed">更新安裝失敗:%1$s</string> <string name="update_install_failed">更新安裝失敗:%1$s</string>
<string name="home_search">搜尋</string> <string name="home_search">搜尋</string>
<string name="home_settings">設定</string> <string name="home_settings">設定</string>
<string name="empty_gamelist">找不到檔案,或者尚未選取遊戲目錄</string> <string name="empty_gamelist">找不到遊戲檔案,或者尚未選取遊戲目錄</string>
<string name="manage_game_folders">管理遊戲資料夾</string> <string name="manage_game_folders">管理遊戲資料夾</string>
<string name="select_games_folder_description">允許 Eden 尋找您的遊戲檔案</string> <string name="select_games_folder_description">允許 Eden 尋找您的遊戲檔案</string>
<string name="add_games_warning">跳過選擇遊戲資料夾?</string> <string name="add_games_warning">跳過選擇遊戲資料夾?</string>
@@ -287,7 +287,7 @@
<string name="warning_skip">跳過</string> <string name="warning_skip">跳過</string>
<string name="warning_cancel">取消</string> <string name="warning_cancel">取消</string>
<string name="install_amiibo_keys">安裝 Amiibo 金鑰</string> <string name="install_amiibo_keys">安裝 Amiibo 金鑰</string>
<string name="install_amiibo_keys_description">若要在遊戲中使用 Amiibo 得先安裝</string> <string name="install_amiibo_keys_description">需要先安裝才能在遊戲中使用Amiibo</string>
<string name="gpu_driver_fetcher">GPU驅動程式下載器</string> <string name="gpu_driver_fetcher">GPU驅動程式下載器</string>
<string name="gpu_driver_manager">GPU驅動程式管理員</string> <string name="gpu_driver_manager">GPU驅動程式管理員</string>
<string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string> <string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string>
@@ -299,7 +299,7 @@
<string name="post_processing_open_list">開啟清單</string> <string name="post_processing_open_list">開啟清單</string>
<string name="post_processing_close_list">收起清單</string> <string name="post_processing_close_list">收起清單</string>
<string name="post_processing_remove_all">移除效果</string> <string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">濾鏡已啟用。請在開啟遊戲前於 後製處理效果 中調整</string> <string name="post_processing_preset_locked">濾鏡已啟用。請在開啟遊戲前於\"後製處理效果\"中調整</string>
<string name="post_processing_preset_modified">數值已變更,與原始濾鏡設定不同</string> <string name="post_processing_preset_modified">數值已變更,與原始濾鏡設定不同</string>
<string name="post_processing_presets">濾鏡</string> <string name="post_processing_presets">濾鏡</string>
<string name="post_processing_preset_new">新增濾鏡</string> <string name="post_processing_preset_new">新增濾鏡</string>
@@ -314,40 +314,49 @@
<string name="frame_gen_per_game_description">調整此遊戲的影格生成設定</string> <string name="frame_gen_per_game_description">調整此遊戲的影格生成設定</string>
<string name="frame_gen_description">使用Lossless Scaling在已渲染的影格之間插入補間影格。啟用此功能時,會強制採用FIFO垂直同步</string> <string name="frame_gen_description">使用Lossless Scaling在已渲染的影格之間插入補間影格。啟用此功能時,會強制採用FIFO垂直同步</string>
<string name="frame_gen_multiplier">影格倍率</string> <string name="frame_gen_multiplier">影格倍率</string>
<string name="frame_gen_multiplier_description">設定在每個已渲染的影格中要顯示的影格數。數值越高所需的 GPU 運算時間也會按比例增加。若要求的影格數超過裝置顯示器能呈現的數量將會降低模擬速度</string> <string name="frame_gen_multiplier_description">設定在每個已渲染的影格中要顯示的影格數,數值越高所需的GPU運算時間也會按比例增加。若要求的影格數超過裝置顯示器能呈現的數量將會降低模擬速度</string>
<string name="frame_gen_multiplier_2x">2x</string> <string name="frame_gen_multiplier_2x">2x</string>
<string name="frame_gen_multiplier_3x">3x</string> <string name="frame_gen_multiplier_3x">3x</string>
<string name="frame_gen_multiplier_4x">4x</string> <string name="frame_gen_multiplier_4x">4x</string>
<string name="frame_gen_target_rate">目標影格率</string> <string name="frame_gen_target_rate">目標影格率</string>
<string name="frame_gen_target_rate_description">選擇裝置顯示器能實際呈現的影格率。之後倍率器會自動升高或降低以維持影格率。如果某個調整導致遊戲本身運作變慢,則會自動復原該設定</string> <string name="frame_gen_target_rate_description">選擇裝置顯示器能實際呈現的影格率,之後倍率器會自動升高或降低以維持影格率。如果某個調整導致遊戲本身運作變慢,則會自動復原該設定</string>
<string name="frame_gen_target_rate_off">使用固定倍率</string> <string name="frame_gen_target_rate_off">使用固定倍率</string>
<string name="frame_gen_target_rate_60">60 FPS</string> <string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string> <string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string> <string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on">開啟</string>
<string name="frame_gen_off">關閉</string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自動</string>
<string name="frame_gen_quick_description">開啟或關閉影格生成後選擇影格倍率</string>
<string name="frame_gen_target_rate_quick_description">選擇要生成的目標影格率,倍率器會自動調整</string>
<string name="frame_gen_flow_scale_quick_description">用來預測影格之間動態的解析度,較低的數值可以減少GPU運算時間</string>
<string name="frame_gen_queue_target">影格佇列目標</string> <string name="frame_gen_queue_target">影格佇列目標</string>
<string name="frame_gen_queue_target_description">最多允許多少個已完成的影格在顯示器前等待,較大的佇列可以吸收 GPU 突發負載,但會增加輸入延遲</string> <string name="frame_gen_queue_target_description">最多允許多少個已完成的影格在顯示器前等待,較大的佇列可以吸收 GPU 突發負載,但會增加輸入延遲</string>
<string name="frame_gen_queue_target_0">最低延遲(無緩衝)</string> <string name="frame_gen_queue_target_0">最低延遲(無緩衝)</string>
<string name="frame_gen_queue_target_1">平衡(1影格)</string> <string name="frame_gen_queue_target_1">平衡(1影格)</string>
<string name="frame_gen_queue_target_2">最流暢(2影格)</string> <string name="frame_gen_queue_target_2">最流暢(2影格)</string>
<string name="frame_gen_flow_scale_auto">配合遊戲調整運動預測</string> <string name="frame_gen_flow_scale_auto">配合遊戲調整運動預測</string>
<string name="frame_gen_flow_scale_auto_description">以遊戲實際渲染的解析度而非升頻後的輸出進行運動預測。由於升頻後不會增加任何動態細節,因此不會影響準確度</string> <string name="frame_gen_flow_scale_auto_description">以遊戲實際渲染的解析度而非升頻後的輸出進行運動預測,由於升頻後不會增加任何動態細節,因此不會影響準確度</string>
<string name="frame_gen_flow_scale">運動預測解析度</string> <string name="frame_gen_flow_scale">運動預測解析度</string>
<string name="frame_gen_flow_scale_description">光流處理的解析度,以輸出解析度的比例表示。降低此設定值是減少效能負載最有效的方法</string> <string name="frame_gen_flow_scale_description">光流處理的解析度,以輸出解析度的比例表示,降低此設定值是減少效能負載最有效的方法</string>
<string name="frame_gen_unsupported">無法使用影格生成</string> <string name="frame_gen_unsupported">無法使用影格生成</string>
<string name="frame_gen_unsupported_description">此GPU驅動程式缺少Lossless Scaling著色器所需的Vulkan記憶體模型或半精度浮點數(float16)支援</string>
<string name="lossless_scaling_setup_description">可選擇安裝自己擁有的 Lossless.dll 以在之後啟用影格生成功能</string> <string name="lossless_scaling_setup_description">可選擇安裝自己擁有的 Lossless.dll 以在之後啟用影格生成功能</string>
<string name="lossless_scaling_install">安裝 Lossless.dll</string> <string name="lossless_scaling_install">安裝 Lossless.dll</string>
<string name="lossless_scaling_install_description">您需要擁有從合法管道獲得的 Lossless Scaling 並從該軟體的資料夾中取得 Lossless.dll 檔案才能使用影格生成功能</string> <string name="lossless_scaling_install_description">您需要擁有從合法管道獲得的 Lossless Scaling 並從該軟體的資料夾中取得 Lossless.dll 檔案才能使用影格生成功能</string>
<string name="lossless_scaling_replace_description">安裝不同的 Lossless.dll 檔案</string> <string name="lossless_scaling_replace_description">安裝不同的 Lossless.dll 檔案</string>
<string name="frame_generation_support">影格生成</string> <string name="frame_generation_support">影格生成</string>
<string name="frame_generation_supported">支援</string> <string name="frame_generation_supported">支援</string>
<string name="frame_generation_unsupported">不支援(無Vulkan記憶體模型或float16)</string>
<string name="lossless_scaling">Lossless Scaling</string> <string name="lossless_scaling">Lossless Scaling</string>
<string name="lossless_scaling_description">安裝您擁有的Lossless.dll檔案以使用影格生成</string> <string name="lossless_scaling_description">安裝您擁有的Lossless.dll檔案以使用影格生成</string>
<string name="lossless_scaling_installed">已安裝</string> <string name="lossless_scaling_installed">已安裝</string>
<string name="lossless_scaling_not_installed">未安裝</string> <string name="lossless_scaling_not_installed">未安裝</string>
<string name="lossless_scaling_replace">替換</string> <string name="lossless_scaling_replace">更換</string>
<string name="lossless_scaling_remove">刪除</string> <string name="lossless_scaling_remove">刪除</string>
<string name="lossless_scaling_remove_description">刪除已安裝的Lossless.dll及預先編譯的著色器</string> <string name="lossless_scaling_remove_description">刪除已安裝的Lossless.dll及預先編譯的著色器</string>
<string name="lossless_scaling_remove_confirmation">影格生成功能會停止運作直到您重新安裝 Lossless.dll 。您的 Lossless.dll 原始檔並不會被 Eden 修改</string> <string name="lossless_scaling_remove_confirmation">影格生成功能會停止運作直到您重新安裝Lossless.dll,您的Lossless.dll原始檔不會被 Eden 修改</string>
<string name="lossless_scaling_missing">Lossless.dll 未安裝</string> <string name="lossless_scaling_missing">Lossless.dll 未安裝</string>
<string name="lossless_scaling_missing_description">前往 設定 -&gt; Lossless Scaling 安裝以使用影格生成功能</string> <string name="lossless_scaling_missing_description">前往 設定 -&gt; Lossless Scaling 安裝以使用影格生成功能</string>
<string name="lossless_scaling_locked">請先關閉遊戲</string> <string name="lossless_scaling_locked">請先關閉遊戲</string>
@@ -360,9 +369,9 @@
<string name="error_lossless_copy_failed">無法複製選取的檔案</string> <string name="error_lossless_copy_failed">無法複製選取的檔案</string>
<string name="error_lossless_unreadable">無法讀取選取的檔案</string> <string name="error_lossless_unreadable">無法讀取選取的檔案</string>
<string name="error_lossless_not_pe">選取的檔案並不是Windows程式庫。請在Lossless Scaling的安裝位置(資料夾)中選擇Lossless.dll</string> <string name="error_lossless_not_pe">選取的檔案並不是Windows程式庫。請在Lossless Scaling的安裝位置(資料夾)中選擇Lossless.dll</string>
<string name="error_lossless_missing_shaders">這個 Lossless.dll 的副本並未包含影格生成著色器。請更新 Lossless Scaling 後再試一次</string> <string name="error_lossless_missing_shaders">這個Lossless.dll的副本並未包含影格生成著色器。請更新Lossless Scaling後重試</string>
<string name="error_lossless_translation_failed">無法轉譯影格生成著色器。尚不支援此 Lossless Scaling 版本</string> <string name="error_lossless_translation_failed">無法轉譯影格生成著色器,尚不支援此Lossless Scaling版本</string>
<string name="error_lossless_cache_failed">轉譯的著色器無法寫入到儲存空間。請確認手機容量是否充足</string> <string name="error_lossless_cache_failed">轉譯的著色器無法寫入到儲存空間,請確認手機容量是否充足</string>
<string name="advanced_settings">進階設定</string> <string name="advanced_settings">進階設定</string>
<string name="settings_description">調整模擬器設定</string> <string name="settings_description">調整模擬器設定</string>
<string name="search_recently_played">最近遊玩</string> <string name="search_recently_played">最近遊玩</string>
@@ -386,7 +395,7 @@
<string name="install_firmware_description">韌體必須為ZIP壓縮檔,將會用於部分遊戲的啟動</string> <string name="install_firmware_description">韌體必須為ZIP壓縮檔,將會用於部分遊戲的啟動</string>
<string name="firmware_installing">正在安裝韌體</string> <string name="firmware_installing">正在安裝韌體</string>
<string name="firmware_installed_failure">韌體安裝失敗</string> <string name="firmware_installed_failure">韌體安裝失敗</string>
<string name="firmware_installed_failure_description">請確保韌體 nca 檔案位於 zip 壓縮檔的根目錄,然後再試一次。</string> <string name="firmware_installed_failure_description">請確保韌體nca檔案位於zip壓縮檔的根目錄後再試一次</string>
<string name="firmware_uninstalled_failure">韌體解除安裝失敗</string> <string name="firmware_uninstalled_failure">韌體解除安裝失敗</string>
<string name="share_log">分享偵錯記錄</string> <string name="share_log">分享偵錯記錄</string>
<string name="share_log_description">分享Eden的記錄檔以便對相關問題進行偵錯</string> <string name="share_log_description">分享Eden的記錄檔以便對相關問題進行偵錯</string>
@@ -406,7 +415,7 @@
<string name="install_game_content_success_overwrite">%1$d 覆寫成功</string> <string name="install_game_content_success_overwrite">%1$d 覆寫成功</string>
<string name="install_game_content_help_link">https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates</string> <string name="install_game_content_help_link">https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates</string>
<string name="custom_driver_not_supported">您的裝置不支援自訂驅動程式</string> <string name="custom_driver_not_supported">您的裝置不支援自訂驅動程式</string>
<string name="custom_driver_not_supported_description">此裝置不支援注入自訂驅動程式。\n請以後再來查看是否已新增支援!</string> <string name="custom_driver_not_supported_description">此裝置不支援注入自訂驅動程式\n請以後再來查看是否已新增支援!</string>
<string name="manage_yuzu_data">管理Eden資料</string> <string name="manage_yuzu_data">管理Eden資料</string>
<string name="manage_yuzu_data_description">安裝韌體、金鑰,導入/導出使用者資料及安裝其它項目!</string> <string name="manage_yuzu_data_description">安裝韌體、金鑰,導入/導出使用者資料及安裝其它項目!</string>
<string name="game_folders">遊戲資料夾</string> <string name="game_folders">遊戲資料夾</string>
@@ -437,8 +446,8 @@
<string name="keys_failed">金鑰安裝失敗</string> <string name="keys_failed">金鑰安裝失敗</string>
<string name="keys_install_success">金鑰安裝成功</string> <string name="keys_install_success">金鑰安裝成功</string>
<string name="error_keys_copy_failed">一個或多個金鑰安裝失敗</string> <string name="error_keys_copy_failed">一個或多個金鑰安裝失敗</string>
<string name="error_keys_invalid_filename">請確保金鑰檔案具有.keys副檔名後重試。</string> <string name="error_keys_invalid_filename">請確保金鑰檔案具有.keys副檔名後重試</string>
<string name="error_keys_failed_init">金鑰初始化失敗。請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰。</string> <string name="error_keys_failed_init">金鑰初始化失敗,請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰</string>
<!-- Applet launcher strings --> <!-- Applet launcher strings -->
<string name="qlaunch_applet">Qlaunch</string> <string name="qlaunch_applet">Qlaunch</string>
@@ -469,19 +478,19 @@
<string name="licenses_description">這些專案使Eden Android版成為可能</string> <string name="licenses_description">這些專案使Eden Android版成為可能</string>
<string name="build">組建版本</string> <string name="build">組建版本</string>
<string name="user_data">使用者資料</string> <string name="user_data">使用者資料</string>
<string name="user_data_description">導入/導出所有應用程式資料。\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入使用者資料可能會出現問題,建議手動導入所有所需資料。</string> <string name="user_data_description">導入/導出所有應用程式資料\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入使用者資料可能會出現問題,建議手動導入所有所需資料</string>
<string name="exporting_user_data">正在導出使用者資料…</string> <string name="exporting_user_data">正在導出使用者資料…</string>
<string name="importing_user_data">正在導入使用者資料…</string> <string name="importing_user_data">正在導入使用者資料…</string>
<string name="invalid_yuzu_backup">無效的Eden備份</string> <string name="invalid_yuzu_backup">無效的Eden備份</string>
<string name="user_data_export_success">使用者資料導出成功</string> <string name="user_data_export_success">使用者資料導出成功</string>
<string name="user_data_import_success">使用者資料導入成功</string> <string name="user_data_import_success">使用者資料導入成功</string>
<string name="user_data_export_cancelled">導出已取消</string> <string name="user_data_export_cancelled">導出已取消</string>
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案,並再試一次</string> <string name="user_data_import_failed_description">請確保使用者資料夾位於zip壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案後重試</string>
<!-- General settings strings --> <!-- General settings strings -->
<string name="frame_limit_enable">限制模擬速度</string> <string name="frame_limit_enable">限制模擬速度</string>
<string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比</string> <string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比</string>
<string name="frame_limit_slider">限制速度百分比</string> <string name="frame_limit_slider">限制速度百分比</string>
<string name="frame_limit_slider_description">指定模擬速度的百分比。100% 為標準速度,更高或更低的值將會減少或增加速度限制</string> <string name="frame_limit_slider_description">指定模擬速度的百分比,100%為標準速度,更高或更低的值將會減少或增加速度限制</string>
<string name="turbo_speed_limit">加速模式</string> <string name="turbo_speed_limit">加速模式</string>
<string name="turbo_speed_limit_description">當開啟加速模式時,模擬器將以此速度執行</string> <string name="turbo_speed_limit_description">當開啟加速模式時,模擬器將以此速度執行</string>
<string name="slow_speed_limit">慢速模式</string> <string name="slow_speed_limit">慢速模式</string>
@@ -509,16 +518,16 @@
<string name="fast_cpu_time">CPU時脈</string> <string name="fast_cpu_time">CPU時脈</string>
<string name="fast_cpu_time_description">提高模擬CPU回報的時脈,可以減少某些FPS限制。較弱的CPU可能會出現效能下降的情況,部分遊戲可能會出現異常</string> <string name="fast_cpu_time_description">提高模擬CPU回報的時脈,可以減少某些FPS限制。較弱的CPU可能會出現效能下降的情況,部分遊戲可能會出現異常</string>
<string name="custom_cpu_ticks">自訂CPU時脈</string> <string name="custom_cpu_ticks">自訂CPU時脈</string>
<string name="custom_cpu_ticks_description">自訂CPU時脈。更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為77-21000。</string> <string name="custom_cpu_ticks_description">自訂CPU時脈,更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為77-21000</string>
<string name="cpu_ticks">時脈</string> <string name="cpu_ticks">時脈</string>
<string name="memory_layout">記憶體佈局</string> <string name="memory_layout">記憶體布局</string>
<string name="memory_layout_description">(實驗性)更改模擬記憶體佈局。此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在 RAM 為 8GB 或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的CPU後端改為 Dynarmic (JIT)</string> <string name="memory_layout_description">(實驗性)更改模擬記憶體布局,此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在RAM為8GB或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的CPU後端改為Dynarmic (JIT)</string>
<string name="generate">生成</string> <string name="generate">生成</string>
<!-- Network settings strings --> <!-- Network settings strings -->
<string name="web_token">網路令牌</string> <string name="web_token">網路令牌</string>
<string name="web_token_description">用於建立公開大廳的網路令牌。由48個小寫字母a-z組成</string> <string name="web_token_description">用於建立公開大廳的網路令牌,由48個小寫字母a-z組成</string>
<string name="web_username">網路使用者名稱</string> <string name="web_username">網路使用者名稱</string>
<string name="web_username_description">多人遊戲房間中顯示的使用者名稱。必須為4-20個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string> <string name="web_username_description">多人遊戲房間中顯示的使用者名稱。必須為4-20個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
<string name="network">網路</string> <string name="network">網路</string>
@@ -535,11 +544,11 @@
<string name="advanced">進階</string> <string name="advanced">進階</string>
<string name="renderer_accuracy">GPU模式</string> <string name="renderer_accuracy">GPU模式</string>
<string name="renderer_accuracy_description">設定 GPU 模擬的準確度。大多數遊戲在設定為快速時即可正常渲染,但有些需要渲染粒子的遊戲仍需設為準確來避免圖形錯誤</string> <string name="renderer_accuracy_description">設定GPU模擬的準確度,大多數遊戲在設定為快速時即可正常渲染,但有些需要渲染粒子的遊戲仍需設為準確來避免圖形錯誤</string>
<string name="dma_accuracy">DMA準確度</string> <string name="dma_accuracy">DMA準確度</string>
<string name="dma_accuracy_description">控制 DMA 準確度。將準確度設為穩定可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為預設</string> <string name="dma_accuracy_description">控制DMA準確度,將準確度設為穩定可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為預設</string>
<string name="gpu_fence_behavior">GPU 同步柵欄模式</string> <string name="gpu_fence_behavior">GPU 同步柵欄模式</string>
<string name="gpu_fence_behavior_description">控制 GPU 同步柵欄模式。即時是速度最快的選項,但可能會導致一些問題。使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤。準確則是在犧牲部分效能的情況下進一步提升相容性。嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題。預設則會依照 GPU 準確度設定</string> <string name="gpu_fence_behavior_description">控制 GPU 同步柵欄模式,即時是速度最快的選項,但可能會導致一些問題;使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤;準確則是在犧牲部分效能的情況下進一步提升相容性;嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題;預設則會依照 GPU 準確度設定</string>
<string name="anisotropic_filtering">各向異性過濾</string> <string name="anisotropic_filtering">各向異性過濾</string>
<string name="anisotropic_filtering_description">改善斜角檢視時的紋理品質</string> <string name="anisotropic_filtering_description">改善斜角檢視時的紋理品質</string>
<string name="vram_usage_mode">VRAM使用模式</string> <string name="vram_usage_mode">VRAM使用模式</string>
@@ -548,21 +557,19 @@
<string name="accelerate_astc_description">選擇ASTC壓縮紋理的解碼方式:CPU(慢速、穩定)、GPU(快速、推薦)或CPU非同步(無卡頓,可能導致問題)</string> <string name="accelerate_astc_description">選擇ASTC壓縮紋理的解碼方式:CPU(慢速、穩定)、GPU(快速、推薦)或CPU非同步(無卡頓,可能導致問題)</string>
<string name="sync_memory_operations">同步記憶體操作</string> <string name="sync_memory_operations">同步記憶體操作</string>
<string name="sync_memory_operations_description">確保計算和記憶體操作之間的資料一致性。 此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。 使用Unreal Engine 4的遊戲似乎受影響最大</string> <string name="sync_memory_operations_description">確保計算和記憶體操作之間的資料一致性,此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。使用Unreal Engine 4的遊戲似乎受影響最大</string>
<string name="use_disk_shader_cache">磁碟著色器快取</string> <string name="use_disk_shader_cache">磁碟著色器快取</string>
<string name="use_disk_shader_cache_description">將產生的著色器快取儲存至硬碟以減少中斷</string> <string name="use_disk_shader_cache_description">將產生的著色器快取儲存至硬碟以減少中斷</string>
<string name="renderer_force_max_clock">強制使用最大時脈 (僅限Adreno)</string> <string name="renderer_force_max_clock">強制使用最大時脈 (僅限Adreno)</string>
<string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string> <string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string> <string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string>
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string> <string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
<string name="renderer_async_presentation">非同步呈現</string>
<string name="renderer_async_presentation_description">此修改可以透過將渲染移至單獨的 CPU 執行緒來提升性能,但可能會導致圖形問題</string>
<string name="renderer_reactive_flushing">使用重新啟用排清</string> <string name="renderer_reactive_flushing">使用重新啟用排清</string>
<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="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>
<string name="hacks">修改</string> <string name="hacks">修改</string>
@@ -571,30 +578,30 @@
<string name="skip_cpu_inner_invalidation">跳過CPU內部失效處理</string> <string name="skip_cpu_inner_invalidation">跳過CPU內部失效處理</string>
<string name="skip_cpu_inner_invalidation_description">在記憶體更新期間跳過某些CPU端快取的失效處理,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或當機</string> <string name="skip_cpu_inner_invalidation_description">在記憶體更新期間跳過某些CPU端快取的失效處理,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或當機</string>
<string name="fix_bloom_effects">修正光暈特效</string> <string name="fix_bloom_effects">修正光暈特效</string>
<string name="fix_bloom_effects_description">在 薩爾達傳說:織夢島 和 薩爾達傳說:智慧的再現 中 減少光暈模糊的問題並移除 橫衝直撞:狂飆樂園重製版 中的光暈效果 (Adreno A6XX - A7XX/ Turnip)。警告:可能會在其它遊戲中導致圖形異常</string> <string name="fix_bloom_effects_description">在 \"薩爾達傳說:織夢島\" 和 \"薩爾達傳說:智慧的再現\" 中減少光暈模糊的問題並移除 \"橫衝直撞:狂飆樂園重製版\" 中的光暈效果(適用於Adreno A6XX - A7XX/ Turnip)。警告:可能會在其它遊戲中導致圖形異常</string>
<string name="emulate_bgr565">模擬BGR565</string> <string name="emulate_bgr565">模擬BGR565</string>
<string name="emulate_bgr565_description">修正遊戲中出現的顏色反轉、圖形異常或陰影異常</string> <string name="emulate_bgr565_description">修正遊戲中出現的色彩反轉、圖形異常或陰影異常</string>
<string name="rescale_hack">啟用舊版重新縮放處理</string> <string name="rescale_hack">啟用舊版重新縮放處理</string>
<string name="rescale_hack_description">透過快速重新縮放處理來啟用遊戲重新縮放設定階段的舊版處理方式</string> <string name="rescale_hack_description">透過快速重新縮放處理來啟用遊戲重新縮放設定階段的舊版處理方式</string>
<string name="renderer_asynchronous_shaders">使用非同步著色器</string> <string name="renderer_asynchronous_shaders">使用非同步著色器</string>
<string name="renderer_asynchronous_shaders_description">使用非同步編譯著色器。這可能會減少卡頓,但也可能導致圖形錯誤。</string> <string name="renderer_asynchronous_shaders_description">使用非同步編譯著色器,這可能會減少卡頓,但也可能導致圖形錯誤</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle設定</string> <string name="gpu_unswizzle_settings">GPU Unswizzle設定</string>
<string name="gpu_unswizzle_settings_description">設定基於 GPU 的 Unswizzling 參數或完全停用此功能。調整此設定以在性能與紋理載入品質中取得平衡</string> <string name="gpu_unswizzle_settings_description">設定基於GPU的Unswizzling參數或完全停用此功能,調整此設定以在性能與紋理載入品質中取得平衡</string>
<string name="gpu_unswizzle_enable">啟用GPU Unswizzle</string> <string name="gpu_unswizzle_enable">啟用GPU Unswizzle</string>
<string name="gpu_unswizzle_disabled">停用</string> <string name="gpu_unswizzle_disabled">停用</string>
<string name="gpu_unswizzle_texture_size">GPU Unswizzle最大紋理尺寸</string> <string name="gpu_unswizzle_texture_size">GPU Unswizzle最大紋理尺寸</string>
<string name="gpu_unswizzle_texture_size_description">設定 GPU 紋理 Unswizzling 的最大值 (MB)。雖然 GPU 處理中型和大型紋理的速度較快,但 CPU 處理非常小的紋理可能更有效率。整此設定以在 GPU 加速與 CPU 負載之間取得平衡</string> <string name="gpu_unswizzle_texture_size_description">設定 GPU 紋理 Unswizzling 的最大值(MB),雖然 GPU 處理中型和大型紋理的速度較快,但 CPU 處理非常小的紋理可能更有效率。調整此設定以在GPU加速與CPU負載之間取得平衡</string>
<string name="gpu_unswizzle_stream_size">GPU Unswizzle 流大小</string> <string name="gpu_unswizzle_stream_size">GPU Unswizzle Stream大小</string>
<string name="gpu_unswizzle_stream_size_description">設定每個影格處理大型紋理 Unswizzling 的資料上限。數值越高載入紋理的速度越快,但會增加畫面延遲;數值較低則能降低 GPU 負載,不過有可能導致至紋理載入不完全甚至突然出現</string> <string name="gpu_unswizzle_stream_size_description">設定每個影格處理大型紋理Unswizzling的資料上限,數值越高載入紋理的速度越快,但會增加畫面延遲;數值較低則能降低 GPU 負載,不過有可能導致至紋理載入不完全甚至突然出現</string>
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle 塊大小</string> <string name="gpu_unswizzle_chunk_size">GPU Unswizzle Chunk大小</string>
<string name="gpu_unswizzle_chunk_size_description">設定 3D 紋理每批次處理的深度切片數量。增加此數值可提升規格較佳 GPU 的處理效率,但在較低規格的硬體上可能導致卡頓或驅動程式逾時</string> <string name="gpu_unswizzle_chunk_size_description">設定 3D 紋理每批次處理的深度切片數量,增加此數值可提升規格較佳GPU的處理效率,但在較低規格的硬體上可能導致卡頓或驅動程式逾時</string>
<string name="gpu_unswizzle_default_button">預設</string> <string name="gpu_unswizzle_default_button">預設</string>
<string name="extensions">擴充功能</string> <string name="extensions">擴充功能</string>
<string name="dyna_state">擴展動態狀態</string> <string name="dyna_state">擴展動態狀態</string>
<string name="dyna_state_description">控制擴展動態狀態中可使用的功能數量。較高的數值可根據驅動程式支援的動態狀態減少管線編譯的次數。</string> <string name="dyna_state_description">控制擴展動態狀態中可使用的功能數量,較高的數值可根據驅動程式支援的動態狀態減少管線編譯的次數</string>
<string name="disabled">停用</string> <string name="disabled">停用</string>
<string name="vertex_input_dynamic_state">頂點輸入動態狀態</string> <string name="vertex_input_dynamic_state">頂點輸入動態狀態</string>
<string name="vertex_input_dynamic_state_description">啟用此功能可提供更彈性的頂點輸入處理方式,並可能減少頂點/緩衝區的管線編譯時間</string> <string name="vertex_input_dynamic_state_description">啟用此功能可提供更彈性的頂點輸入處理方式,並可能減少頂點/緩衝區的管線編譯時間</string>
@@ -616,19 +623,19 @@
<string name="cpu">CPU</string> <string name="cpu">CPU</string>
<string name="clocks">時脈</string> <string name="clocks">時脈</string>
<string name="use_auto_stub">使用自動存根</string> <string name="use_auto_stub">使用自動存根</string>
<string name="use_auto_stub_description">自動補上缺少的服務和功能。可提高相容性但可能導致當機和穩定性問題。</string> <string name="use_auto_stub_description">自動補上缺少的服務和功能,可提高相容性但可能導致當機和穩定性問題</string>
<string name="gpu">GPU</string> <string name="gpu">GPU</string>
<string name="renderer_api">API</string> <string name="renderer_api">API</string>
<string name="renderer_debug">圖形偵錯</string> <string name="renderer_debug">圖形偵錯</string>
<string name="renderer_debug_description">將圖形API設為慢速偵錯模式</string> <string name="renderer_debug_description">將圖形API設為慢速偵錯模式</string>
<string name="patch_old_qcom_drivers">BCn區塊壓縮紋理修補</string> <string name="patch_old_qcom_drivers">BCn區塊壓縮紋理修補</string>
<string name="patch_old_qcom_drivers_description">覆寫 Adreno GPU 的 BCn 紋理格式自動偵測。通常會根據 Android 版本自動偵測(會在API 28 以上自動啟用)</string> <string name="patch_old_qcom_drivers_description">覆寫Adreno GPU的BCn紋理格式自動偵測。通常會根據Android版本自動偵測(會在API 28以上的裝置自動啟用)</string>
<string name="fastmem">Fastmem</string> <string name="fastmem">Fastmem</string>
<string name="log">日誌</string> <string name="log">日誌</string>
<string name="flush_by_line">按行寫入偵錯日誌</string> <string name="flush_by_line">按行寫入偵錯日誌</string>
<string name="flush_by_line_description">在每行寫入時重新整理偵錯日誌,讓程式在當機或閃退時更容易偵錯。</string> <string name="flush_by_line_description">在寫入一行日誌就立刻更新偵錯記錄,讓程式在當機或閃退時更容易偵錯</string>
<string name="extended_logging">啟用更詳細的日誌記錄</string> <string name="extended_logging">啟用更詳細的日誌記錄</string>
<string name="extended_logging_description">將日誌檔的最大檔案限制從100MB提高到1GB</string> <string name="extended_logging_description">將日誌檔的最大檔案限制從100MB提高到1GB</string>
<string name="log_filter">日誌過濾器</string> <string name="log_filter">日誌過濾器</string>
@@ -641,11 +648,11 @@
<string name="gpu_log_vulkan_calls">記錄Vulkan API呼叫</string> <string name="gpu_log_vulkan_calls">記錄Vulkan API呼叫</string>
<string name="gpu_log_vulkan_calls_description">在環形緩衝區中記錄所有Vulkan API呼叫</string> <string name="gpu_log_vulkan_calls_description">在環形緩衝區中記錄所有Vulkan API呼叫</string>
<string name="gpu_log_shader_dumps">傾印SPIR-V著色器</string> <string name="gpu_log_shader_dumps">傾印SPIR-V著色器</string>
<string name="gpu_log_shader_dumps_description">將重新編譯後的 SPIR-V 二進位檔案 (.spv) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 spirv-dis/spirv-cross/spirv-val 進行檢查</string> <string name="gpu_log_shader_dumps_description">將重新編譯後的SPIR-V二進位檔案(.spv)儲存至Eden資料夾裡的dump資料夾中。可使用 spirv-dis/spirv-cross/spirv-val 進行檢查</string>
<string name="dump_guest_shaders">傾印客體(Maxwell)著色器</string> <string name="dump_guest_shaders">傾印客體(Maxwell)著色器</string>
<string name="dump_guest_shaders_description">將 Maxwell 客體著色器位元組碼檔案 (.ash) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 nvdisasm 進行檢查</string> <string name="dump_guest_shaders_description">將Maxwell客體著色器位元組碼檔案(.ash)儲存至Eden資料夾裡的dump資料夾中。可使用 nvdisasm 進行檢查</string>
<string name="dump_macros">傾印Maxwell巨集</string> <string name="dump_macros">傾印Maxwell巨集</string>
<string name="dump_macros_description">將 Maxwell 巨集程式檔案 (.macro) 儲存至 Eden 資料夾裡的 dump 資料夾中。可使用 envydis 進行檢查</string> <string name="dump_macros_description">將Maxwell巨集程式檔案(.macro)儲存至Eden資料夾裡的dump資料夾中。可使用 envydis 進行檢查</string>
<string name="gpu_log_memory_tracking">追蹤GPU記憶體</string> <string name="gpu_log_memory_tracking">追蹤GPU記憶體</string>
<string name="gpu_log_memory_tracking_description">監控GPU記憶體的分配和釋放</string> <string name="gpu_log_memory_tracking_description">監控GPU記憶體的分配和釋放</string>
<string name="gpu_log_driver_debug">驅動程式偵錯資料</string> <string name="gpu_log_driver_debug">驅動程式偵錯資料</string>
@@ -799,7 +806,7 @@
<string name="recommended_driver">推薦驅動程式:</string> <string name="recommended_driver">推薦驅動程式:</string>
<string name="gpu_model">GPU型號</string> <string name="gpu_model">GPU型號</string>
<string name="unsupported_gpu">不支援的GPU</string> <string name="unsupported_gpu">不支援的GPU</string>
<string name="unsupported_gpu_warning">您的GPU不支援驅動程式注入。不建議嘗試設定自訂驅動程式。</string> <string name="unsupported_gpu_warning">您的GPU不支援驅動程式注入,不建議嘗試設定自訂驅動程式</string>
<!-- Preferences Screen --> <!-- Preferences Screen -->
<string name="preferences_settings">設定</string> <string name="preferences_settings">設定</string>
@@ -580,8 +580,6 @@
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string> <string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
<string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string> <string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string>
<string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string> <string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string>
<string name="renderer_async_presentation">Asynchronous presentation</string>
<string name="renderer_async_presentation_description">This hack can increase performance by moving presentation to a separate CPU thread at the cost of graphical issues.</string>
<string name="renderer_reactive_flushing">Use reactive flushing</string> <string name="renderer_reactive_flushing">Use reactive flushing</string>
<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>
@@ -595,6 +593,10 @@
<string name="fast_gpu_time_description">Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch\'s clocks.</string> <string name="fast_gpu_time_description">Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch\'s clocks.</string>
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string> <string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string> <string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
<string name="nce_invalidation_gpu_readback">GPU Readback Before NCE Invalidation</string>
<string name="nce_invalidation_gpu_readback_description">Temporary. Fix Ender Magnolia (and other UE titles) crashes. May reduce performance. Improvement in progress.</string>
<string name="nce_runtime_nro_patch">Patch Runtime NROs for NCE</string>
<string name="nce_runtime_nro_patch_description">Temporary. Fix Biomutant boot. May impact performance. Improvement in progress.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string> <string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string> <string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string> <string name="emulate_bgr565">Emulate BGR565</string>
+1 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -19,11 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
} }
} }
void ADSP::Shutdown() {
audio_renderer->Stop();
opus_decoder->Shutdown();
}
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
return *audio_renderer; return *audio_renderer;
} }
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -45,7 +45,6 @@ public:
explicit ADSP(Core::System& system, Sink::Sink& sink); explicit ADSP(Core::System& system, Sink::Sink& sink);
~ADSP() = default; ~ADSP() = default;
void Shutdown();
AudioRenderer::AudioRenderer& AudioRenderer(); AudioRenderer::AudioRenderer& AudioRenderer();
OpusDecoder::OpusDecoder& OpusDecoder(); OpusDecoder::OpusDecoder& OpusDecoder();
@@ -34,19 +34,26 @@ void AudioRenderer::Start() {
mailbox.Send(Direction::DSP, Message::InitializeOK); mailbox.Send(Direction::DSP, Message::InitializeOK);
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) { if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!"); LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
"message response from ADSP!");
return;
} }
running = true;
} }
void AudioRenderer::Stop() { void AudioRenderer::Stop() {
if (main_thread.joinable()) { if (!running) {
main_thread.request_stop(); return;
}
mailbox.Send(Direction::DSP, Message::Shutdown); mailbox.Send(Direction::DSP, Message::Shutdown);
if (mailbox.Receive(Direction::Host, main_thread.get_stop_token()) != Message::Shutdown) { if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!"); LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
"message response from ADSP!");
} }
main_thread.request_stop();
main_thread.join(); main_thread.join();
}
for (auto& stream : streams) { for (auto& stream : streams) {
if (stream) { if (stream) {
stream->Stop(); stream->Stop();
@@ -54,6 +61,7 @@ void AudioRenderer::Stop() {
stream = nullptr; stream = nullptr;
} }
} }
running = false;
} }
void AudioRenderer::Signal() { void AudioRenderer::Signal() {
@@ -61,12 +69,13 @@ void AudioRenderer::Signal() {
Send(Direction::DSP, Message::Render); Send(Direction::DSP, Message::Render);
} }
void AudioRenderer::Wait(std::stop_token stop_token) { void AudioRenderer::Wait() {
auto msg = mailbox.Receive(Direction::Host, stop_token); auto msg = Receive(Direction::Host);
if (msg != Message::RenderResponse) { if (msg != Message::RenderResponse) {
LOG_ERROR(Service_Audio, LOG_ERROR(Service_Audio,
"Did not receive the expected render response from the AudioRenderer! Expected " "Did not receive the expected render response from the AudioRenderer! Expected "
"{}, got {}", Message::RenderResponse, msg); "{}, got {}",
Message::RenderResponse, msg);
} }
PostDSPClearCommandBuffer(); PostDSPClearCommandBuffer();
} }
@@ -138,9 +147,7 @@ void AudioRenderer::Main(std::stop_token stop_token) {
constexpr u64 max_process_time{2'304'000ULL}; constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg = mailbox.Receive(Direction::DSP, stop_token); auto msg{mailbox.Receive(Direction::DSP)};
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Message::Shutdown: case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown); mailbox.Send(Direction::Host, Message::Shutdown);
@@ -63,15 +63,13 @@ public:
*/ */
void Start(); void Start();
void NotifyShutdown();
/** /**
* Stop the AudioRenderer. * Stop the AudioRenderer.
*/ */
void Stop(); void Stop();
void Signal(); void Signal();
void Wait(std::stop_token stop_token); void Wait();
void Send(Direction dir, u32 message); void Send(Direction dir, u32 message);
u32 Receive(Direction dir); u32 Receive(Direction dir);
@@ -104,6 +102,8 @@ private:
Mailbox mailbox; Mailbox mailbox;
/// Main thread /// Main thread
std::jthread main_thread{}; std::jthread main_thread{};
/// The current state
std::atomic<bool> running{};
/// Shared memory of input command buffers, set by host, read by DSP /// Shared memory of input command buffers, set by host, read by DSP
std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
/// The command lists to process /// The command lists to process
+21 -31
View File
@@ -41,16 +41,19 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
} }
OpusDecoder::~OpusDecoder() { OpusDecoder::~OpusDecoder() {
if (main_thread.joinable()) { if (!running) {
init_thread.request_stop();
return;
}
// Shutdown the thread // Shutdown the thread
Send(Direction::DSP, Message::Shutdown); Send(Direction::DSP, Message::Shutdown);
auto msg = Receive(Direction::Host);
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
Message::ShutdownOK, msg);
main_thread.request_stop(); main_thread.request_stop();
auto msg = Receive(Direction::Host, main_thread.get_stop_token());
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
main_thread.join(); main_thread.join();
} else { running = false;
init_thread.request_stop();
}
} }
void OpusDecoder::Send(Direction dir, u32 message) { void OpusDecoder::Send(Direction dir, u32 message) {
@@ -70,6 +73,7 @@ void OpusDecoder::Init(std::stop_token stop_token) {
return; return;
} }
main_thread = std::jthread([this](std::stop_token st) { Main(st); }); main_thread = std::jthread([this](std::stop_token st) { Main(st); });
running = true;
Send(Direction::Host, Message::StartOK); Send(Direction::Host, Message::StartOK);
} }
@@ -78,15 +82,12 @@ void OpusDecoder::Main(std::stop_token stop_token) {
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token); auto msg = Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Message::Shutdown: case Shutdown:
Send(Direction::Host, Message::ShutdownOK); Send(Direction::Host, Message::ShutdownOK);
return; return;
case Message::GetWorkBufferSize: { case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]); auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count)); ASSERT(IsValidChannelCount(channel_count));
@@ -95,7 +96,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeOK); Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break; } break;
case Message::InitializeDecodeObject: { case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1]; auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -112,7 +113,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeDecodeObjectOK); Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break; } break;
case Message::ShutdownDecodeObject: { case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -122,7 +123,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownDecodeObjectOK); Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break; } break;
case Message::DecodeInterleaved: { case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs(); auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
@@ -160,19 +161,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::DecodeInterleavedOK); Send(Direction::Host, Message::DecodeInterleavedOK);
} break; } break;
case Message::MapMemory: { case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK); Send(Direction::Host, Message::MapMemoryOK);
} break; } break;
case Message::UnmapMemory: { case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0]; [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK); Send(Direction::Host, Message::UnmapMemoryOK);
} break; } break;
case Message::GetWorkBufferSizeForMultiStream: { case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]); auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]); auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
@@ -183,7 +184,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK); Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break; } break;
case Message::InitializeMultiStreamDecodeObject: { case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1]; auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]); auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -210,7 +211,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK); Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break; } break;
case Message::ShutdownMultiStreamDecodeObject: { case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1]; [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -220,7 +221,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK); Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break; } break;
case Message::DecodeInterleavedForMultiStream: { case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs(); auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0]; auto buffer = shared_memory->host_send_data[0];
@@ -265,15 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
} }
} }
void OpusDecoder::Shutdown() {
if (init_thread.joinable()) {
init_thread.request_stop();
init_thread.join(); //must explicitly wait for init to join
}
if (main_thread.joinable()) {
main_thread.request_stop();
Send(Direction::DSP, Message::Shutdown);
}
}
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder
+3 -3
View File
@@ -57,7 +57,7 @@ public:
~OpusDecoder(); ~OpusDecoder();
bool IsRunning() const noexcept { bool IsRunning() const noexcept {
return main_thread.joinable(); return running;
} }
void Send(Direction dir, u32 message); void Send(Direction dir, u32 message);
@@ -67,8 +67,6 @@ public:
shared_memory = &shared_memory_; shared_memory = &shared_memory_;
} }
void Shutdown();
private: private:
/** /**
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
@@ -87,6 +85,8 @@ private:
std::jthread init_thread{}; std::jthread init_thread{};
/// Main thread /// Main thread
std::jthread main_thread{}; std::jthread main_thread{};
/// The current state
bool running{};
/// Structure shared with the host, input data set by the host before sending a mailbox message, /// Structure shared with the host, input data set by the host before sending a mailbox message,
/// and the responses are written back by the OpusDecoder. /// and the responses are written back by the OpusDecoder.
SharedMemory* shared_memory{}; SharedMemory* shared_memory{};
-1
View File
@@ -33,7 +33,6 @@ void AudioCore::CreateSinks() {
void AudioCore::Shutdown() { void AudioCore::Shutdown() {
audio_manager->Shutdown(); audio_manager->Shutdown();
adsp->Shutdown();
} }
AudioManager& AudioCore::GetAudioManager() { AudioManager& AudioCore::GetAudioManager() {
+2 -2
View File
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
const auto timed_out = events.Wait(l, std::chrono::seconds{2}); const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) { if (events.CheckAudioEventSet(Event::Type::Max)) {
break; break;
} }
@@ -35,9 +35,9 @@ AudioManager::AudioManager() {
} }
void AudioManager::Shutdown() { void AudioManager::Shutdown() {
events.SetAudioEvent(Event::Type::Max, true);
if (thread.joinable()) { if (thread.joinable()) {
thread.request_stop(); thread.request_stop();
events.SetAudioEvent(Event::Type::Max, true);
thread.join(); thread.join();
} }
} }
+3 -1
View File
@@ -39,7 +39,9 @@ class AudioManager {
public: public:
explicit AudioManager(); explicit AudioManager();
/// @brief Shutdown the audio manager. /**
* Shutdown the audio manager.
*/
void Shutdown(); void Shutdown();
/** /**
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())}; auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())}; auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) { if (in_use == buffer_params->in_use && !buffer_unmapped) {
error_info.error_code = ResultSuccess; error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0); error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2; out_status.writeOffset = current_state->last_pos2;
+20 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
}; };
struct DeviceInParameter { struct DeviceInParameter {
/* 0x000 */ u8 name[0x100]; /* 0x000 */ char name[0x100];
/* 0x100 */ u32 input_count; /* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs; /* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ u8 unk10A[0x1]; /* 0x10A */ char unk10A[0x1];
/* 0x10B */ bool downmix_enabled; /* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff; /* 0x10C */ std::array<f32, 4> downmix_coeff;
}; };
@@ -43,7 +43,7 @@ public:
struct DeviceState { struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info; /* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff; /* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ u8 unk18[0x18]; /* 0x18 */ char unk18[0x18];
}; };
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!"); static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos; /* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format; /* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs; /* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ u8 in_use; /* 0x22 */ bool in_use;
/* 0x23 */ u8 unk23[0x5]; /* 0x23 */ char unk23[0x5];
}; };
static_assert(sizeof(CircularBufferInParameter) == 0x28, static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!"); "CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2; /* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos; /* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos; /* 0x08 */ u32 last_pos;
/* 0x0C */ u8 unk0C[0x4]; /* 0x0C */ char unk0C[0x4];
/* 0x10 */ AddressInfo address_info; /* 0x10 */ AddressInfo address_info;
}; };
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!"); static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter { struct InParameter {
/* 0x000 */ Type type; /* 0x000 */ Type type;
/* 0x001 */ u8 in_use; /* 0x001 */ bool in_use;
/* 0x004 */ u32 node_id; /* 0x004 */ u32 node_id;
/* 0x008 */ u8 unk08[0x18]; /* 0x008 */ char unk08[0x18];
union { union {
/* 0x020 */ DeviceInParameter device; /* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer; /* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus { struct OutStatus {
/* 0x00 */ u32 writeOffset; /* 0x00 */ u32 writeOffset;
/* 0x04 */ u8 unk04[0x1C]; /* 0x04 */ char unk04[0x1C];
}; // size == 0x20 }; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!"); static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,18 +162,19 @@ public:
u8* GetParameter(); u8* GetParameter();
protected: protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink /// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{}; std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink /// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{}; std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
/// Type of this sink parameter{};
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+14 -14
View File
@@ -100,9 +100,8 @@ 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_} : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
, 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,
@@ -407,7 +406,7 @@ void System::Finalize() {
return; return;
} }
if (IsActive()) { if (active) {
Stop(); Stop();
} }
@@ -434,12 +433,14 @@ 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) {
@@ -479,7 +480,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result; return result;
} }
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer, result = info_updater.UpdateEffects(effect_context, active, 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!");
@@ -581,16 +582,16 @@ u32 System::GetRenderingDevice() const {
} }
bool System::IsActive() const { bool System::IsActive() const {
return state == State::Started; return active;
} }
void System::SendCommandToDsp() { void System::SendCommandToDsp() {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
if (initialized) { if (initialized) {
if (IsActive()) { if (active) {
terminate_event.Reset(); terminate_event.Reset();
const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id); const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
u64 command_size{0}; u64 command_size{0};
if (remaining_command_count) { if (remaining_command_count) {
@@ -617,17 +618,16 @@ void System::SendCommandToDsp() {
auto time_limit{ auto time_limit{
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
(static_cast<f32>(render_time_limit_percent) / 100.0f))}; (static_cast<f32>(render_time_limit_percent) / 100.0f))};
core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id, audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
translated_addr, command_size, applet_resource_user_id, process_handle,
time_limit, applet_resource_user_id, reset_command_buffers);
process_handle, reset_command_buffers);
reset_command_buffers = false; reset_command_buffers = false;
command_buffer_size = command_size; command_buffer_size = command_size;
if (remaining_command_count == 0) { if (remaining_command_count == 0) {
adsp_rendered_event->Signal(core.Kernel()); adsp_rendered_event->Signal(core.Kernel());
} }
} else { } else {
core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id); audio_renderer.ClearRemainCommandCount(session_id);
terminate_event.Set(); terminate_event.Set();
} }
} }
@@ -738,7 +738,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 = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id); render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
frames_elapsed++; frames_elapsed++;
return command_buffer.size; return command_buffer.size;
+2 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -222,6 +219,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? /// Is this system currently active?
+26 -23
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -11,70 +11,73 @@
#include "audio_core/renderer/system_manager.h" #include "audio_core/renderer/system_manager.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/core_timing.h" #include "core/core_timing.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
SystemManager::SystemManager(Core::System& core_) SystemManager::SystemManager(Core::System& core_)
: core{core_} : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
{
}
SystemManager::~SystemManager() { SystemManager::~SystemManager() {
Stop(); Stop();
} }
void SystemManager::InitializeUnsafe() { void SystemManager::InitializeUnsafe() {
if (!thread.joinable()) { if (!active) {
core.AudioCore().ADSP().AudioRenderer().Start(); active = true;
thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() { audio_renderer.Start();
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer(); thread = std::jthread([this](std::stop_token stop_token) {
auto const stop_token = thread.get_stop_token(); Common::SetCurrentThreadName("AudioRenderSystemManager");
while (!stop_token.stop_requested()) { Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
while (active && !stop_token.stop_requested()) {
{ {
std::scoped_lock lk{mutex}; std::scoped_lock l{mutex1};
for (auto system : systems) for (auto system : systems)
system->SendCommandToDsp(); system->SendCommandToDsp();
} }
audio_renderer.Signal(); audio_renderer.Signal();
audio_renderer.Wait(stop_token); audio_renderer.Wait();
} }
}); });
} }
} }
void SystemManager::Stop() { void SystemManager::Stop() {
core.AudioCore().ADSP().AudioRenderer().Stop(); if (active) {
if (thread.joinable()) { active = false;
thread.request_stop(); thread.request_stop();
thread.join(); thread.join();
audio_renderer.Stop();
} }
} }
bool SystemManager::Add(System& system_) { bool SystemManager::Add(System& system_) {
std::scoped_lock lk{mutex}; std::scoped_lock l2{mutex2};
if (systems.size() >= MaxRendererSessions) { if (systems.size() + 1 > MaxRendererSessions) {
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!"); LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
return false; return false;
} }
systems.push_back(&system_); {
if (!systems.empty()) { std::scoped_lock l{mutex1};
if (systems.empty())
InitializeUnsafe(); InitializeUnsafe();
} }
systems.push_back(&system_);
return true; return true;
} }
bool SystemManager::Remove(System& system_) { bool SystemManager::Remove(System& system_) {
std::scoped_lock lk{mutex}; std::scoped_lock l2{mutex2};
{
std::scoped_lock l{mutex1};
if (systems.remove(&system_) == 0) { if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!"); LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false; return false;
} }
if (systems.empty()) {
Stop();
} }
if (systems.empty())
Stop();
return true; return true;
} }
+9 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -75,8 +75,14 @@ private:
std::list<System*> systems{}; std::list<System*> systems{};
/// Main worker thread for generating command lists /// Main worker thread for generating command lists
std::jthread thread; std::jthread thread;
/// Mutex for the reading and adding/removing systems /// Mutex for the systems
std::mutex mutex{}; std::mutex mutex1{};
/// Mutex for adding/removing systems
std::mutex mutex2{};
/// Is the system manager thread active?
std::atomic<bool> active{};
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+7 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -109,6 +109,12 @@ private:
if (read_index == producer.index.load(std::memory_order::acquire)) { if (read_index == producer.index.load(std::memory_order::acquire)) {
return false; return false;
} }
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else if constexpr (Mode == PopMode::WaitWithStopToken) { } else if constexpr (Mode == PopMode::WaitWithStopToken) {
// Wait until the queue is not empty. // Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex}; std::unique_lock lock{consumer.cv_mutex};
@@ -118,12 +124,6 @@ private:
if (stop_token.stop_requested()) { if (stop_token.stop_requested()) {
return false; return false;
} }
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else { } else {
static_assert(Mode < PopMode::Count, "Invalid PopMode."); static_assert(Mode < PopMode::Count, "Invalid PopMode.");
} }
+16 -15
View File
@@ -171,8 +171,13 @@ struct Values {
SwitchableSetting<bool> enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet}; SwitchableSetting<bool> enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet};
// Audio // Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", SwitchableSetting<AudioEngine> sink_id{linkage,
Category::Audio, Specialization::RuntimeList}; #ifdef __ANDROID__
AudioEngine::Sdl3,
#else
AudioEngine::Auto,
#endif
"output_engine", Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_output_device_id{ SwitchableSetting<std::string> audio_output_device_id{
linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList}; linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
SwitchableSetting<std::string> audio_input_device_id{ SwitchableSetting<std::string> audio_input_device_id{
@@ -472,13 +477,8 @@ struct Values {
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
Category::Renderer}; Category::Renderer};
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage, SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage, true, "use_asynchronous_gpu_emulation",
#ifdef __ANDROID__ Category::Renderer};
false,
#else
true,
#endif
"use_asynchronous_gpu_emulation", Category::Renderer};
// *nix platforms may have issues with the borderless windowed fullscreen mode. // *nix platforms may have issues with the borderless windowed fullscreen mode.
// Default to exclusive fullscreen on these platforms for now. // Default to exclusive fullscreen on these platforms for now.
SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage, SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
@@ -537,7 +537,7 @@ struct Values {
"vram_usage_mode", "vram_usage_mode",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu, SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Cpu,
"nvdec_emulation", Category::RendererAdvanced}; "nvdec_emulation", Category::RendererAdvanced};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
@@ -639,9 +639,13 @@ struct Values {
Specialization::Default, Specialization::Default,
true, true,
true}; true};
SwitchableSetting<bool> nce_invalidation_gpu_readback{
linkage, false, "nce_invalidation_gpu_readback", Category::RendererHacks};
SwitchableSetting<bool> nce_runtime_nro_patch{
linkage, false, "nce_runtime_nro_patch", Category::RendererHacks};
SwitchableSetting<bool> async_presentation{linkage, SwitchableSetting<bool> async_presentation{linkage,
#ifdef __ANDROID__ #ifdef __ANDROID__
false, true,
#else #else
false, false,
#endif #endif
@@ -653,9 +657,6 @@ struct Values {
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565", SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks}; Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks}; Category::RendererHacks};
@@ -932,7 +933,7 @@ struct Values {
// Network // Network
Setting<std::string> network_interface{linkage, std::string(), "network_interface", Setting<std::string> network_interface{linkage, std::string(), "network_interface",
Category::Network}; Category::Network};
SwitchableSetting<bool> airplane_mode{linkage, false, "airplane_mode", Category::Network}; SwitchableSetting<bool> airplane_mode{linkage, true, "airplane_mode", Category::Network};
// WebService // WebService
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url", Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
+1 -1
View File
@@ -150,7 +150,7 @@ ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, Scale
ENUM(AntiAliasing, None, Fxaa, Smaa); ENUM(AntiAliasing, None, Fxaa, Smaa);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked); ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE); ENUM(AppletMode, HLE, LLE, Disabled);
ENUM(SpirvOptimizeMode, Never, OnLoad, Always); ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
ENUM(GpuClock, Normal, Boost, Overclock) ENUM(GpuClock, Normal, Boost, Overclock)
ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge) ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge)
+23 -45
View File
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system} : kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
, fs_controller{system} reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
, hid_core{kernel}
, cpu_manager{system}
, reporter{system}
, profile_manager{}
{}
u64 program_id; u64 program_id;
@@ -129,12 +124,6 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
applet_manager.emplace(system);
frontend_applets.emplace(system);
apm_controller.emplace(core_timing);
arp_manager.emplace();
profile_manager.emplace();
// Create a default fs if one doesn't already exist. // Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) { if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -144,7 +133,7 @@ struct System::Impl {
} }
// Create default implementations of applets if one is not provided. // Create default implementations of applets if one is not provided.
frontend_applets->SetDefaultAppletsIfMissing(); frontend_applets.SetDefaultAppletsIfMissing();
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -387,7 +376,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
@@ -439,27 +428,13 @@ struct System::Impl {
core_timing.SyncPause(false); core_timing.SyncPause(false);
Network::CancelPendingSocketOperations(); Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true); kernel.SuspendEmulation(true);
kernel.ShutdownCores();
// Notify services helpers of shutdown
audio_core->Shutdown();
// Wait for threads/services to join
kernel.CloseServices(); kernel.CloseServices();
kernel.ShutdownCores();
// service shutdown
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
frontend_applets.reset();
applet_manager.reset();
apm_controller.reset();
arp_manager.reset();
profile_manager.reset();
fs_controller.Reset(); fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
core_timing.ClearPendingEvents(); core_timing.ClearPendingEvents();
core_timing.Reset();
app_loader.reset(); app_loader.reset();
audio_core.reset(); audio_core.reset();
gpu_core.reset(); gpu_core.reset();
@@ -476,6 +451,9 @@ struct System::Impl {
room_member->SendGameInfo(game_info); room_member->SendGameInfo(game_info);
} }
// Reset all glue registrations
arp_manager.ResetAll();
LOG_DEBUG(Core, "Shutdown OK"); LOG_DEBUG(Core, "Shutdown OK");
} }
@@ -504,13 +482,13 @@ struct System::Impl {
CpuManager cpu_manager; CpuManager cpu_manager;
Reporter reporter; Reporter reporter;
/// Applets /// Applets
std::optional<Service::AM::AppletManager> applet_manager; Service::AM::AppletManager applet_manager;
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets; Service::AM::Frontend::FrontendAppletHolder frontend_applets;
/// APM (Performance) services /// APM (Performance) services
std::optional<Service::APM::Controller> apm_controller; Service::APM::Controller apm_controller{core_timing};
/// Service State /// Service State
std::optional<Service::Glue::ARPManager> arp_manager; Service::Glue::ARPManager arp_manager;
std::optional<Service::Account::ProfileManager> profile_manager; Service::Account::ProfileManager profile_manager;
/// Network instance /// Network instance
Network::NetworkInstance network_instance; Network::NetworkInstance network_instance;
Core::SpeedLimiter speed_limiter; Core::SpeedLimiter speed_limiter;
@@ -837,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
} }
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets->SetFrontendAppletSet(std::move(set)); impl->frontend_applets.SetFrontendAppletSet(std::move(set));
} }
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return *impl->frontend_applets; return impl->frontend_applets;
} }
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return *impl->frontend_applets; return impl->frontend_applets;
} }
Service::AM::AppletManager& System::GetAppletManager() { Service::AM::AppletManager& System::GetAppletManager() {
return *impl->applet_manager; return impl->applet_manager;
} }
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -890,27 +868,27 @@ const Reporter& System::GetReporter() const {
} }
Service::Glue::ARPManager& System::GetARPManager() { Service::Glue::ARPManager& System::GetARPManager() {
return *impl->arp_manager; return impl->arp_manager;
} }
const Service::Glue::ARPManager& System::GetARPManager() const { const Service::Glue::ARPManager& System::GetARPManager() const {
return *impl->arp_manager; return impl->arp_manager;
} }
Service::APM::Controller& System::GetAPMController() { Service::APM::Controller& System::GetAPMController() {
return *impl->apm_controller; return impl->apm_controller;
} }
const Service::APM::Controller& System::GetAPMController() const { const Service::APM::Controller& System::GetAPMController() const {
return *impl->apm_controller; return impl->apm_controller;
} }
Service::Account::ProfileManager& System::GetProfileManager() { Service::Account::ProfileManager& System::GetProfileManager() {
return *impl->profile_manager; return impl->profile_manager;
} }
const Service::Account::ProfileManager& System::GetProfileManager() const { const Service::Account::ProfileManager& System::GetProfileManager() const {
return *impl->profile_manager; return impl->profile_manager;
} }
void System::SetExitLocked(bool locked) { void System::SetExitLocked(bool locked) {
+2 -2
View File
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
void CoreTiming::Reset() { void CoreTiming::Reset() {
paused = true; paused = true;
if (timer_thread.joinable()) {
timer_thread.request_stop();
pause_event.Set(); pause_event.Set();
event.Set(); event.Set();
if (timer_thread.joinable()) {
timer_thread.request_stop();
timer_thread.join(); timer_thread.join();
} }
has_started = false; has_started = false;
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept { void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked()); ASSERT(this->IsLocked());
if (m_woken_dummy_threads.size() > 0) {
for (auto* thread : m_woken_dummy_threads) for (auto* thread : m_woken_dummy_threads)
thread->DummyThreadEndWait(kernel); thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear(); m_woken_dummy_threads.clear();
} }
}
} // namespace Kernel } // namespace Kernel
+1
View File
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
KThread* cur_child = nullptr; KThread* cur_child = nullptr;
{ {
KScopedLightLock proc_lk(process->GetListLock()); KScopedLightLock proc_lk(process->GetListLock());
auto& thread_list = process->GetThreadList(); auto& thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
+8 -2
View File
@@ -1236,13 +1236,18 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request. // Determine if this is the first termination request.
const bool first_request = [&]() -> bool {
// Perform an atomic compare-and-swap from false to true. // Perform an atomic compare-and-swap from false to true.
bool expected = false; bool expected = false;
return m_termination_requested.compare_exchange_strong(expected, true);
}();
// If this is the first request, start termination procedure. // If this is the first request, start termination procedure.
if (m_termination_requested.compare_exchange_strong(expected, true)) { if (first_request) {
// If the thread is in initialized state, just change state to terminated. // If the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) { if (this->GetState() == ThreadState::Initialized) {
return m_thread_state = ThreadState::Terminated; m_thread_state = ThreadState::Terminated;
return ThreadState::Terminated;
} }
// Register the terminating dpc. // Register the terminating dpc.
@@ -1276,6 +1281,7 @@ namespace Kernel {
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true); m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
} }
} }
return this->GetState(); return this->GetState();
} }
+5 -34
View File
@@ -4,9 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "common/thread.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task.h" #include "core/hle/kernel/k_worker_task.h"
@@ -28,33 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
} }
} }
KWorkerTaskManager::KWorkerTaskManager(KernelCore& kernel) { KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
Common::SetCurrentThreadName("KWorkerManager");
while (!stop_token.stop_requested()) {
KWorkerTask* t;
{
std::unique_lock lk{m_task_mutex};
m_task_cv.wait(lk, m_waiting_thread.get_stop_token(), [&]() {
return !m_task_queue.empty();
});
if (stop_token.stop_requested())
break;
t = m_task_queue.back();
m_task_queue.pop_back();
}
t->DoWorkerTask(kernel);
}
});
}
KWorkerTaskManager::~KWorkerTaskManager() {
if (m_waiting_thread.joinable()) {
m_waiting_thread.request_stop();
m_task_cv.notify_one();
m_waiting_thread.join();
}
}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count); ASSERT(type <= WorkerType::Count);
@@ -63,11 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
{ m_waiting_thread.QueueWork([&kernel, task]() {
std::scoped_lock lk{m_task_mutex}; // Do the task.
m_task_queue.emplace_back(task); task->DoWorkerTask(kernel);
} });
m_task_cv.notify_one();
} }
} // namespace Kernel } // namespace Kernel
+4 -11
View File
@@ -1,13 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include <thread>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
@@ -23,17 +18,15 @@ public:
Count, Count,
}; };
KWorkerTaskManager(KernelCore& kernel); KWorkerTaskManager();
~KWorkerTaskManager();
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task); static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
private: private:
void AddTask(KernelCore& kernel, KWorkerTask* task); void AddTask(KernelCore& kernel, KWorkerTask* task);
std::jthread m_waiting_thread;
std::mutex m_task_mutex; private:
std::condition_variable_any m_task_cv; Common::ThreadWorker m_waiting_thread;
std::vector<KWorkerTask*> m_task_queue;
}; };
} // namespace Kernel } // namespace Kernel
+12 -17
View File
@@ -80,10 +80,7 @@ struct KernelCore::Impl {
// so it will be statically given a TLS slot anyways. // so it will be statically given a TLS slot anyways.
static inline thread_local ThreadLocalData tls_data = {}; static inline thread_local ThreadLocalData tls_data = {};
explicit Impl(Core::System& system_, KernelCore& kernel_) explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
: worker_task_manager{kernel_}
, system{system_}
{
tls_data.lock = true; tls_data.lock = true;
} }
@@ -970,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer; return *impl->hardware_timer;
} }
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() { KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const { const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
void KernelCore::PrepareReschedule(std::size_t id) { void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1008,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get(); auto* manager = server_manager.get();
if (!impl->is_shutting_down) {
{
std::scoped_lock lk{impl->server_lock}; std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager)); impl->server_managers.emplace_back(std::move(server_manager));
} }
manager->LoopProcess(); manager->LoopProcess();
} }
@@ -1256,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
} }
void KernelCore::ShutdownCores() { void KernelCore::ShutdownCores() {
// Notify shutdown (pre-emptively)
for (auto& sm : impl->server_managers) {
sm->NotifyShutdown();
}
impl->TerminateAllProcesses(); impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this}; KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) for (auto* thread : impl->shutdown_threads)
+3 -2
View File
@@ -175,8 +175,9 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread. /// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id); void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer* ObjectListContainer(); KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
const KAutoObjectWithListContainer& ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking /// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown. /// leaks after emulation has been shutdown.
+3 -4
View File
@@ -151,8 +151,7 @@ public:
const bool is_initialized = this->IsInitialized(); const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0; uintptr_t arg = 0;
if (is_initialized) { if (is_initialized) {
if (auto const olc = kernel.ObjectListContainer(); olc) kernel.ObjectListContainer().Unregister(this);
olc->Unregister(this);
arg = this->GetPostDestroyArgument(); arg = this->GetPostDestroyArgument();
this->Finalize(kernel); this->Finalize(kernel);
} }
@@ -176,7 +175,7 @@ public:
public: public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) { static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size); kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer()->Initialize(); kernel.ObjectListContainer().Initialize();
} }
static Derived* Create(KernelCore& kernel) { static Derived* Create(KernelCore& kernel) {
@@ -187,7 +186,7 @@ public:
} }
static void Register(KernelCore& kernel, Derived* obj) { static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer()->Register(obj); return kernel.ObjectListContainer().Register(obj);
} }
static size_t GetObjectSize(KernelCore& kernel) { static size_t GetObjectSize(KernelCore& kernel) {
+6 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,9 +15,13 @@ void LoopProcess(Core::System& system) {
WindowSystem window_system(system); WindowSystem window_system(system);
ButtonPoller button_poller(system, window_system); ButtonPoller button_poller(system, window_system);
EventObserver event_observer(system, window_system); EventObserver event_observer(system, window_system);
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService("appletOE", std::make_shared<IApplicationProxyService>(system, window_system)); server_manager->RegisterNamedService(
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService(
"appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+7 -8
View File
@@ -26,25 +26,24 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
m_window_system.SetEventObserver(this); m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent)); m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() { m_thread = std::jthread([this](std::stop_token stop_token) {
auto const stop_token = m_stop_source.get_token(); Common::SetCurrentThreadName("am:EventObserver");
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token); auto* signaled_holder = this->WaitSignaled(stop_token);
if (stop_token.stop_requested() || !signaled_holder) if (!signaled_holder)
break; break;
this->Process(signaled_holder); this->Process(signaled_holder);
} }
m_process_cv.notify_one();
}); });
} }
EventObserver::~EventObserver() { EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish. // Signal thread and wait for processing to finish.
m_stop_source.request_stop(); if (m_thread.joinable()) {
// Signal thread and wait for processing to finish.
m_thread.request_stop();
m_wakeup_event.Signal(m_system.Kernel()); m_wakeup_event.Signal(m_system.Kernel());
{ m_thread.join();
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
} }
// Free remaining owned sessions. // Free remaining owned sessions.
+1 -5
View File
@@ -6,8 +6,6 @@
#pragma once #pragma once
#include <condition_variable>
#include <stop_token>
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
@@ -64,9 +62,7 @@ private:
MultiWait m_deferred_wait_list; MultiWait m_deferred_wait_list;
// Processing thread. // Processing thread.
std::stop_source m_stop_source{}; std::jthread m_thread{};
std::mutex m_process_mutex;
std::condition_variable m_process_cv{};
}; };
} // namespace Service::AM } // namespace Service::AM
+3 -2
View File
@@ -250,9 +250,10 @@ std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<
return std::make_shared<PhotoViewer>(system, applet, mode, *frontend.photo_viewer); return std::make_shared<PhotoViewer>(system, applet, mode, *frontend.photo_viewer);
case AppletId::NetConnect: case AppletId::NetConnect:
return std::make_shared<NetConnect>(system, applet, mode, *frontend.net_connect); return std::make_shared<NetConnect>(system, applet, mode, *frontend.net_connect);
case AppletId::None:
return nullptr;
default: default:
LOG_ERROR(Service_AM, "No backend implementation exists for applet_id={:02X} program_id={:016X}" UNIMPLEMENTED_MSG("No frontend implementation exists for applet_id={:02X} program_id={:016X}! Using stub applet.", static_cast<u8>(id), applet->program_id);
"Falling back to stub applet", static_cast<u8>(id), applet->program_id);
return std::make_shared<StubApplet>(system, applet, id, mode); return std::make_shared<StubApplet>(system, applet, id, mode);
} }
} }
+5 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
} }
TimeManager::TimeManager(Core::System& system) TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_time_zone_binary{system} : m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker} system,
{ m_steady_clock_resource,
m_file_timestamp_worker} {
m_time_m = m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
+31 -31
View File
@@ -20,17 +20,15 @@
namespace Service::Glue::Time { namespace Service::Glue::Time {
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker) TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
: m_system{system} FileTimestampWorker& file_timestamp_worker)
, m_ctx{m_system, "Glue:TimeWorker"} : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")} "Glue:TimeWorker:Event")},
, m_steady_clock_resource{steady_clock_resource} m_steady_clock_resource{steady_clock_resource},
, m_file_timestamp_worker{file_timestamp_worker} m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")} "Glue:TimeWorker:SteadyClockTimerEvent")},
, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")} m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
, m_alarm_worker{m_system, m_steady_clock_resource} m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
, m_pm_state_change_handler{m_alarm_worker}
{
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
"Time::SteadyClockEvent", "Time::SteadyClockEvent",
[this](s64 time, [this](s64 time,
@@ -49,16 +47,14 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
} }
TimeWorker::~TimeWorker() { TimeWorker::~TimeWorker() {
// Wait for processing to stop
m_stop_source.request_stop();
m_local_clock_event->Signal(m_system.Kernel()); m_local_clock_event->Signal(m_system.Kernel());
m_network_clock_event->Signal(m_system.Kernel()); m_network_clock_event->Signal(m_system.Kernel());
m_ephemeral_clock_event->Signal(m_system.Kernel()); m_ephemeral_clock_event->Signal(m_system.Kernel());
std::this_thread::sleep_for(std::chrono::milliseconds(16));
m_thread.request_stop();
m_event->Signal(m_system.Kernel()); m_event->Signal(m_system.Kernel());
{ m_thread.join();
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
}
m_ctx.CloseEvent(m_event); m_ctx.CloseEvent(m_event);
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
@@ -126,8 +122,13 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
} }
void TimeWorker::StartThread() { void TimeWorker::StartThread() {
m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() { m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
auto const stop_token = m_stop_source.get_token(); }
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
enum class EventType : s32 { enum class EventType : s32 {
Exit = 0, Exit = 0,
@@ -140,18 +141,18 @@ void TimeWorker::StartThread() {
UpdateFileTimestamp = 7, UpdateFileTimestamp = 7,
AutoCorrect = 8, AutoCorrect = 8,
}; };
s32 index{}; s32 index{};
if (m_pm_state_change_handler.m_priority != 0) { if (m_pm_state_change_handler.m_priority != 0) {
// TODO: gIPmModuleService::GetEvent() 1 // TODO: gIPmModuleService::GetEvent() 1
index = WaitAny( index = WaitAny(m_system.Kernel(),
m_system.Kernel(),
&m_event->GetReadableEvent(), // 0 &m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1 &m_alarm_worker.GetEvent() // 1
); );
} else { } else {
// TODO: gIPmModuleService::GetEvent() 1 // TODO: gIPmModuleService::GetEvent() 1
index = WaitAny( index = WaitAny(m_system.Kernel(),
m_system.Kernel(),
&m_event->GetReadableEvent(), // 0 &m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1 &m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
@@ -164,10 +165,7 @@ void TimeWorker::StartThread() {
); );
} }
if (stop_token.stop_requested()) switch (static_cast<EventType>(index)) {
break;
switch (EventType(index)) {
case EventType::Exit: case EventType::Exit:
return; return;
@@ -260,6 +258,7 @@ void TimeWorker::StartThread() {
case EventType::UpdateFileTimestamp: case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel()); m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime(); m_file_timestamp_worker.SetFilesystemPosixTime();
break; break;
@@ -267,21 +266,22 @@ void TimeWorker::StartThread() {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel()); m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{}; bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction)); R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{}; Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break; break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} }
m_process_cv.notify_one();
});
} }
} // namespace Service::Glue::Time } // namespace Service::Glue::Time
+4 -8
View File
@@ -1,12 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h" #include "core/hle/service/glue/time/alarm_worker.h"
@@ -37,17 +33,17 @@ public:
void StartThread(); void StartThread();
private:
template <typename T> template <typename T>
T GetSettingsItemValue(const std::string& category, const std::string& name); T GetSettingsItemValue(const std::string& category, const std::string& name);
void ThreadFunc(std::stop_token stop_token);
Core::System& m_system; Core::System& m_system;
KernelHelpers::ServiceContext m_ctx; KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::mutex m_process_mutex; std::jthread m_thread;
std::condition_variable m_process_cv;
std::stop_source m_stop_source;
Kernel::KEvent* m_event{}; Kernel::KEvent* m_event{};
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
+32 -2
View File
@@ -16,6 +16,10 @@
#include "core/hle/service/ro/ro_types.h" #include "core/hle/service/ro/ro_types.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#ifdef HAS_NCE
#include "core/arm/nce/patcher.h"
#include "core/hle/kernel/k_shared_memory.h"
#endif
namespace Service::RO { namespace Service::RO {
@@ -386,7 +390,7 @@ public:
R_SUCCEED(); R_SUCCEED();
} }
Result MapManualLoadModuleMemory(u64* out_address, size_t context_id, u64 nro_address, Result MapManualLoadModuleMemory(Kernel::KernelCore& kernel, u64* out_address, size_t context_id, u64 nro_address,
u64 nro_size, u64 bss_address, u64 bss_size) { u64 nro_size, u64 bss_address, u64 bss_size) {
// Get context. // Get context.
ProcessContext* context = this->GetContextById(context_id); ProcessContext* context = this->GetContextById(context_id);
@@ -421,7 +425,33 @@ public:
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size), R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
std::addressof(ro_size), std::addressof(rw_size), std::addressof(ro_size), std::addressof(rw_size),
nro_info->base_address, nro_size, bss_size)); nro_info->base_address, nro_size, bss_size));
#ifdef HAS_NCE
if (Settings::values.nce_runtime_nro_patch.GetValue()) {
if (Settings::IsNceEnabled()) {
auto* process = context->GetProcess();
auto& memory = process->GetMemory();
std::vector<u8> image(total_size);
memory.ReadBlock(nro_info->base_address, image.data(), rx_size);
Kernel::CodeSet::Segment code{.size = static_cast<u32>(rx_size)};
Core::NCE::Patcher patch;
patch.PatchText(image, code);
patch.RelocateAndCopy(nro_info->base_address, code, image, nullptr);
const u64 patch_address = nro_info->base_address + total_size;
const size_t patch_size = patch.GetSectionSize();
constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute;
auto* patch_memory = Kernel::KSharedMemory::Create(kernel);
R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size));
std::memcpy(patch_memory->GetPointer(), image.data() + total_size, patch_size);
R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size));
R_TRY(patch_memory->Map(*process, patch_address, patch_size, permission));
memory.WriteBlock(nro_info->base_address, image.data(), rx_size);
}
}
#endif
// Set NRO perms. // Set NRO perms.
R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size, R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size,
rw_size + bss_size)); rw_size + bss_size));
@@ -531,7 +561,7 @@ public:
Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid, Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, R_RETURN(m_ro->MapManualLoadModuleMemory(system.Kernel(), out_load_address.Get(), m_context_id, nro_address,
nro_size, bss_address, bss_size)); nro_size, bss_address, bss_size));
} }
+3 -12
View File
@@ -98,10 +98,8 @@ ServerManager::~ServerManager() {
// Signal stop. // Signal stop.
m_stop_source.request_stop(); m_stop_source.request_stop();
m_wakeup_event->Signal(m_system.Kernel()); m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
// Wait for processing to stop.
// Wait for processing to stop. Schedule
m_stopped.Wait(); m_stopped.Wait();
m_threads.clear(); m_threads.clear();
@@ -255,14 +253,6 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
} }
} }
/// @brief Notifies that the system is shutting down (pre-emptively terminate threads)
void ServerManager::NotifyShutdown() {
m_stop_source.request_stop();
// Wake them up regardless
m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
}
Result ServerManager::LoopProcess() { Result ServerManager::LoopProcess() {
SCOPE_EXIT { SCOPE_EXIT {
m_stopped.Set(); m_stopped.Set();
@@ -295,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
this->LinkDeferred(); this->LinkDeferred();
// If we're done, return before we start waiting. // If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) if (m_stop_source.stop_requested()) {
return nullptr; return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) { if (selected == std::addressof(*m_wakeup_holder)) {
-4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -52,7 +49,6 @@ public:
Result LoopProcess(); Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads); void StartAdditionalHostThreads(const char* name, size_t num_threads);
void NotifyShutdown();
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
+1 -1
View File
@@ -117,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"friends", &Friend::LoopProcess}, {"friends", &Friend::LoopProcess},
{"settings", &Set::LoopProcess}, {"settings", &Set::LoopProcess},
{"psc", &PSC::LoopProcess}, {"psc", &PSC::LoopProcess},
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess}, {"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess}, {"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess}, {"jit", &JIT::LoopProcess},
@@ -152,7 +153,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"usb", &USB::LoopProcess}, {"usb", &USB::LoopProcess},
{"i2c", &I2C::LoopProcess}, {"i2c", &I2C::LoopProcess},
{"gpio", &GPIO::LoopProcess}, {"gpio", &GPIO::LoopProcess},
{"glue", &Glue::LoopProcess},
}) })
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
} }
+6 -4
View File
@@ -305,14 +305,16 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
SM::~SM() = default; SM::~SM() = default;
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
Kernel::KEvent* deferral_event{}; Kernel::KEvent* deferral_event{};
server_manager->ManageDeferral(&deferral_event); server_manager->ManageDeferral(&deferral_event);
system.ServiceManager().SetDeferralEvent(deferral_event); service_manager.SetDeferralEvent(deferral_event);
auto sm_service = std::make_shared<SM>(system.ServiceManager(), system); auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
server_manager->ManageNamedPort("sm:", [sm_service] { server_manager->ManageNamedPort("sm:", [sm_service] { return sm_service; });
return sm_service;
});
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+27 -17
View File
@@ -11,7 +11,6 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/vi/conductor.h" #include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h" #include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h" #include "core/hle/service/vi/display_list.h"
@@ -30,26 +29,21 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
}); });
if (system.IsMulticore()) { if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
m_signal.Set(); m_signal.Set();
return std::chrono::nanoseconds(this->GetNextTicks()); return std::chrono::nanoseconds(this->GetNextTicks());
}); });
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() { m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
auto const stop_token = m_thread.get_stop_token();
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!stop_token.stop_requested()) {
m_signal.Wait();
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
break;
}
this->ProcessVsync();
}
});
} else { } else {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync(); this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks()); return std::chrono::nanoseconds(this->GetNextTicks());
}); });
@@ -60,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
Conductor::~Conductor() { Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event); m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_thread.joinable()) {
if (m_system.IsMulticore()) {
m_thread.request_stop(); m_thread.request_stop();
m_signal.Set(); m_signal.Set();
m_thread.join();
} }
} }
@@ -89,6 +83,22 @@ void Conductor::ProcessVsync() {
} }
} }
void Conductor::VsyncThread(std::stop_token token) {
Common::SetCurrentThreadName("VSyncThread");
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!token.stop_requested()) {
m_signal.Wait();
if (m_system.IsShuttingDown()) {
return;
}
this->ProcessVsync();
}
}
s64 Conductor::GetNextTicks() const { s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values; const auto& settings = Settings::values;
auto speed_scale = 1.f; auto speed_scale = 1.f;
+1 -1
View File
@@ -757,7 +757,7 @@ struct Memory::Impl {
}; };
auto& gpu = system.GPU(); auto& gpu = system.GPU();
gpu_device_memory->ApplyOpOnPointer( gpu_device_memory->ApplyOpOnPointer(
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); }); p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size, true); });
} }
Core::System& system; Core::System& system;
-14
View File
@@ -1,15 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// gcc14 bug
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h" #include "core/memory/dmnt_cheat_types.h"
@@ -1275,8 +1266,3 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
} // namespace Core::Memory } // namespace Core::Memory
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
+4 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -254,16 +254,14 @@ struct UnrecognizedInstruction {
struct CheatVmOpcode { struct CheatVmOpcode {
bool begin_conditional_block{}; bool begin_conditional_block{};
std::variant< std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
std::monostate,
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
> opcode{}; opcode{};
}; };
class DmntCheatVm { class DmntCheatVm {
@@ -170,19 +170,13 @@ template<>
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
auto const marked_bit = *ctx.conf.page_table_marked_bit; code.bt(page, *ctx.conf.page_table_marked_bit);
if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) {
code.test(page, s32(1 << marked_bit));
code.jnz(abort, code.T_NEAR);
} else {
code.bt(page, marked_bit);
code.jc(abort, code.T_NEAR); code.jc(abort, code.T_NEAR);
} }
}
// mask away attributes // mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) { if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) { } else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask); code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.mov(tmp, ctx.conf.page_table_pointer_mask);
@@ -79,31 +79,29 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid); UpdateButtonLifo(npad_entry, data->aruid);
} }
} }
}
void NpadAbstractButtonHandler::UpdateCoreBatteryState() { void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid); UpdateButtonLifo(npad_entry, data->aruid);
} }
} }
}
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) { auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
if (data == nullptr) {
return;
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid); UpdateButtonLifo(npad_entry, aruid);
} }
}
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+9 -9
View File
@@ -194,6 +194,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"), INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"),
tr("Skips certain cache invalidations during memory updates, reducing CPU usage and " tr("Skips certain cache invalidations during memory updates, reducing CPU usage and "
"improving latency. This may cause soft-crashes.")); "improving latency. This may cause soft-crashes."));
INSERT(Settings, nce_invalidation_gpu_readback,
tr("GPU Readback Before NCE Invalidation"),
tr("Flushes GPU buffer data back to guest memory before NCE invalidates the whole "
"guest page, preserving it while the CPU store proceeds."));
INSERT(Settings, nce_runtime_nro_patch,
tr("Patch Runtime NROs for NCE"),
tr("Applies NCE code patches to runtime-loaded NRO modules."
"This is required for some games to run correctly."));
INSERT(Settings, vsync_mode, tr("VSync Mode:"), INSERT(Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n" "refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n"
@@ -271,12 +279,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"quality and performance consistency in some games.")); "quality and performance consistency in some games."));
INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout.")); INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout."));
INSERT(Settings, rescale_hack, tr("Enable Legacy Rescale Pass"),
tr("May fix rescale issues in some games by relying on behavior from the previous "
"implementation.\n"
"Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and "
"grey texture flicker on Nvidia GPUs in Luigis Mansion 3."));
// Renderer (Extensions) // Renderer (Extensions)
INSERT(Settings, dyna_state, tr("Extended Dynamic State"), INSERT(Settings, dyna_state, tr("Extended Dynamic State"),
tr("Controls the number of features that can be used in Extended Dynamic State.\n" tr("Controls the number of features that can be used in Extended Dynamic State.\n"
@@ -342,9 +344,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"it bypasses such prompts and directly exits the emulation.")); "it bypasses such prompts and directly exits the emulation."));
INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
tr("Hides the mouse after 2.5s of inactivity.")); tr("Hides the mouse after 2.5s of inactivity."));
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
tr("Forcibly disables the use of the controller applet in emulated programs.\n"
"When a program attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings, check_for_updates, tr("Check for updates"), INSERT(UISettings, check_for_updates, tr("Check for updates"),
tr("Whether or not to check for updates upon startup.")); tr("Whether or not to check for updates upon startup."));
@@ -380,6 +379,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
{ {
PAIR(AppletMode, HLE, tr("Custom frontend")), PAIR(AppletMode, HLE, tr("Custom frontend")),
PAIR(AppletMode, LLE, tr("Real applet")), PAIR(AppletMode, LLE, tr("Real applet")),
PAIR(AppletMode, Disabled, tr("Disabled")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(), translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(),
-2
View File
@@ -127,8 +127,6 @@ struct Values {
Setting<bool> hide_mouse{ Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true}; true, true};
Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
Category::UiGeneral};
// Set when Vulkan is known to crash the application // Set when Vulkan is known to crash the application
bool has_broken_vulkan = false; bool has_broken_vulkan = false;
+2 -5
View File
@@ -66,17 +66,14 @@ void EmuThread::run() {
} else { } else {
QtCommon::system->Pause(); QtCommon::system->Pause();
m_stopped.Set(); m_stopped.Set();
EmulationPaused(lk); EmulationPaused(lk);
m_should_run_cv.wait(lk, stop_token, [&] { m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; });
return m_should_run;
});
EmulationResumed(lk); EmulationResumed(lk);
} }
} }
// Shutdown the main emulated process // Shutdown the main emulated process
// Needs to pause first, we may not have paused before shutting down.
QtCommon::system->Pause();
QtCommon::system->DetachDebugger(); QtCommon::system->DetachDebugger();
QtCommon::system->ShutdownMainProcess(); QtCommon::system->ShutdownMainProcess();
} }
@@ -511,12 +511,13 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
} }
// Start "before" first PHI; advance on phi_arg == 0 // Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = size_t(-1); size_t phi_index = static_cast<size_t>(-1);
ctx.PatchDeferredPhi([&ctx, &phi_index, phi_insts = std::move(phi_instructions)](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
ctx.PatchDeferredPhi([&](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) { if (phi_arg == 0) {
++phi_index; ++phi_index;
} }
IR::Inst* phi = phi_insts[phi_index]; IR::Inst* phi = phi_instructions[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent }; return { ctx.Def(phi->Arg(phi_arg)), parent };
}); });
} }
@@ -301,7 +301,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info); Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
Optimization::TexturePass(env, program, normalized_host_info); Optimization::TexturePass(env, program, normalized_host_info);
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) { if (Settings::values.resolution_info.active) {
Optimization::RescalingPass(program); Optimization::RescalingPass(program);
} }
Optimization::DeadCodeEliminationPass(program); Optimization::DeadCodeEliminationPass(program);
+4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 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
@@ -28,6 +31,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
void VendorWorkaroundPass(IR::Program& program); void VendorWorkaroundPass(IR::Program& program);
void VerificationPass(const IR::Program& program); void VerificationPass(const IR::Program& program);
bool HasBrokenPattern(const IR::Program& program);
// Dual Vertex // Dual Vertex
void VertexATransformPass(IR::Program& program); void VertexATransformPass(IR::Program& program);
@@ -67,19 +67,12 @@ void VisitMark(IR::Block& block, IR::Inst& inst) {
if (must_patch_outside) { if (must_patch_outside) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, it}; IR::IREmitter ir{block, it};
if (Settings::values.rescale_hack.GetValue()) {
const IR::F32 new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_inst, up_factor)};
inst.ReplaceUsesWith(converted);
} else {
IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)}; IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})}; const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())}; const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_bitcast, up_factor)}; const IR::Value converted{ir.FPMul(new_bitcast, up_factor)};
inst.ReplaceUsesWith(converted); inst.ReplaceUsesWith(converted);
} }
}
break; break;
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -76,4 +79,44 @@ void VendorWorkaroundPass(IR::Program& program) {
} }
} }
bool HasBrokenPattern(const IR::Program& program) {
if (program.stage != Stage::Fragment) {
return false;
}
for (const IR::Block* block : program.post_order_blocks) {
for (const IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::ShuffleIndex:
case IR::Opcode::ShuffleUp:
case IR::Opcode::ShuffleDown:
case IR::Opcode::ShuffleButterfly:
break;
default:
continue;
}
const IR::Value shuffle_arg{inst.Arg(0)};
if (shuffle_arg.IsImmediate()) {
continue;
}
const IR::Inst* bitcast{shuffle_arg.InstRecursive()};
if (!bitcast || bitcast->GetOpcode() != IR::Opcode::BitCastU32F32) {
continue;
}
const IR::Value bitcast_arg{bitcast->Arg(0)};
if (bitcast_arg.IsImmediate()) {
continue;
}
const IR::Inst* attribute{bitcast_arg.InstRecursive()};
if (!attribute || attribute->GetOpcode() != IR::Opcode::GetAttribute) {
continue;
}
const IR::Attribute attr{attribute->Arg(0).Attribute()};
if (attr == IR::Attribute::PositionX || attr == IR::Attribute::PositionY) {
return true;
}
}
}
return false;
}
} // namespace Shader::Optimization } // namespace Shader::Optimization
+15 -3
View File
@@ -226,7 +226,19 @@ struct GPU::Impl {
} }
/// Notify rasterizer that any caches of the specified region should be invalidated /// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size) { void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) {
if (Settings::values.nce_invalidation_gpu_readback.GetValue()) {
VideoCore::RasterizerInterface* rasterizer = renderer->ReadRasterizer();
if (preserve_gpu_writes && rasterizer->MustFlushRegion(addr, size, VideoCommon::CacheType::BufferCache)) {
const u64 fence = RequestSyncOperation([rasterizer, addr, size] {
rasterizer->FlushRegion(addr, size, VideoCommon::CacheType::BufferCache);
rasterizer->OnCacheInvalidation(addr, size);
});
gpu_thread.TickGPU(is_async);
WaitForSyncOperation(fence);
return;
}
}
gpu_thread.InvalidateRegion(addr, size); gpu_thread.InvalidateRegion(addr, size);
} }
@@ -518,8 +530,8 @@ void GPU::FlushRegion(DAddr addr, u64 size) {
impl->FlushRegion(addr, size); impl->FlushRegion(addr, size);
} }
void GPU::InvalidateRegion(DAddr addr, u64 size) { void GPU::InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) {
impl->InvalidateRegion(addr, size); impl->InvalidateRegion(addr, size, preserve_gpu_writes);
} }
bool GPU::OnCPUWrite(DAddr addr, u64 size) { bool GPU::OnCPUWrite(DAddr addr, u64 size) {
+1 -1
View File
@@ -248,7 +248,7 @@ public:
void FlushRegion(DAddr addr, u64 size); void FlushRegion(DAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be invalidated /// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size); void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes = false);
/// Notify rasterizer that CPU is trying to write this area. It returns true if the area is /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is
/// sensible, false otherwise, addr and size must be a valid combination /// sensible, false otherwise, addr and size must be a valid combination
@@ -217,13 +217,15 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
blit_swapchain.DrawToFrame(device, rasterizer, frame, framebuffers, blit_swapchain.DrawToFrame(device, rasterizer, frame, framebuffers,
render_window.GetFramebufferLayout(), swapchain.GetImageCount(), render_window.GetFramebufferLayout(),
present_manager.SwapchainImageCount(),
swapchain.GetImageViewFormat()); swapchain.GetImageViewFormat());
#ifdef HAS_LSFG #ifdef HAS_LSFG
void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames())); void(frame_gen.WantedGenerations(present_manager.MaxExtraFrames()));
frame_gen.Process(device, frame, swapchain.GetImageFormat(), GuestExtent(framebuffers)); frame_gen.Process(device, frame, present_manager.SwapchainImageFormat(),
GuestExtent(framebuffers));
const size_t generated_frames = frame_gen.GeneratedFrameCount(); const size_t generated_frames = frame_gen.GeneratedFrameCount();
for (size_t generation = 0; generation < generated_frames; ++generation) { for (size_t generation = 0; generation < generated_frames; ++generation) {
@@ -23,6 +23,7 @@
#include "shader_recompiler/environment.h" #include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h" #include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/ir_opt/passes.h"
#include "shader_recompiler/program_header.h" #include "shader_recompiler/program_header.h"
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
@@ -826,6 +827,11 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
} }
if (device.GetDriverID() != VK_DRIVER_ID_NVIDIA_PROPRIETARY && Shader::Optimization::HasBrokenPattern(programs[index])) {
LOG_WARNING(Render_Vulkan, "0x{:016x}: Skipping shader with known issues", key.unique_hashes[index]);
return nullptr;
}
if (Settings::values.dump_guest_shaders) { if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hashes[index]); env.Dump(hash, key.unique_hashes[index]);
} }
@@ -23,6 +23,7 @@ namespace Vulkan {
namespace { namespace {
constexpr size_t MAX_FRAMES_IN_FLIGHT = 7; constexpr size_t MAX_FRAMES_IN_FLIGHT = 7;
constexpr u32 MAX_PRESENT_ATTEMPTS = 3;
#ifdef HAS_LSFG #ifdef HAS_LSFG
static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS); static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
#endif #endif
@@ -372,12 +373,33 @@ void PresentManager::SetImageCount() {
#else #else
image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT); image_count = std::min<size_t>(swapchain.GetImageCount(), MAX_FRAMES_IN_FLIGHT);
#endif #endif
swapchain_image_count = swapchain.GetImageCount();
swapchain_image_format = swapchain.GetImageFormat();
}
void PresentManager::DiscardFrame(Frame* frame) {
static constexpr VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const VkSemaphore render_ready = *frame->render_ready;
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 1U,
.pWaitSemaphores = &render_ready,
.pWaitDstStageMask = &wait_stage,
.commandBufferCount = 0U,
.pCommandBuffers = nullptr,
.signalSemaphoreCount = 0U,
.pSignalSemaphores = nullptr,
};
std::scoped_lock submit_lock{scheduler.submit_mutex};
void(device.GetGraphicsQueue().Submit(submit_info, *frame->present_done));
} }
void PresentManager::CopyToSwapchain(Frame* frame) { void PresentManager::CopyToSwapchain(Frame* frame) {
bool requires_recreation = false; bool requires_recreation = false;
while (true) { for (u32 attempt = 0; attempt < MAX_PRESENT_ATTEMPTS; ++attempt) {
try { try {
// Recreate surface and swapchain if needed. // Recreate surface and swapchain if needed.
if (requires_recreation) { if (requires_recreation) {
@@ -397,6 +419,8 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
requires_recreation = true; requires_recreation = true;
} }
} }
DiscardFrame(frame);
} }
void PresentManager::CopyToSwapchainImpl(Frame* frame) { void PresentManager::CopyToSwapchainImpl(Frame* frame) {
@@ -6,6 +6,7 @@
#pragma once #pragma once
#include <atomic>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <boost/container/deque.hpp> #include <boost/container/deque.hpp>
@@ -68,6 +69,9 @@ public:
/// How many additional frames can be queued without stalling the render thread /// How many additional frames can be queued without stalling the render thread
[[nodiscard]] size_t MaxExtraFrames() const; [[nodiscard]] size_t MaxExtraFrames() const;
[[nodiscard]] std::size_t SwapchainImageCount() const { return swapchain_image_count; }
[[nodiscard]] VkFormat SwapchainImageFormat() const { return swapchain_image_format; }
private: private:
void PresentThread(std::stop_token token); void PresentThread(std::stop_token token);
@@ -77,6 +81,8 @@ private:
void RecreateSwapchain(Frame* frame); void RecreateSwapchain(Frame* frame);
void DiscardFrame(Frame* frame);
void SetImageCount(); void SetImageCount();
private: private:
@@ -100,7 +106,9 @@ private:
bool blit_supported; bool blit_supported;
bool storage_supported; bool storage_supported;
bool use_present_thread; bool use_present_thread;
std::size_t image_count{}; std::atomic<std::size_t> image_count{};
std::atomic<std::size_t> swapchain_image_count{};
std::atomic<VkFormat> swapchain_image_format{};
}; };
} // namespace Vulkan } // namespace Vulkan
+3 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project // SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,7 +11,6 @@
class QCheckBox; class QCheckBox;
class QLineEdit; class QLineEdit;
class QComboBox;
class QDateTimeEdit; class QDateTimeEdit;
namespace Core { namespace Core {
class System; class System;

Some files were not shown because too many files have changed in this diff Show More