Merge branch 'frame_pacing' of https://git.eden-emu.dev/eden-emu/eden into frame_pacing
This commit is contained in:
commit
9da9cfa38f
|
|
@ -0,0 +1,26 @@
|
|||
From b3622608433c183ba868a1dc8dd9cf285eb3b916 Mon Sep 17 00:00:00 2001
|
||||
From: Dario Petrillo <dario.pk1@gmail.com>
|
||||
Date: Thu, 27 Nov 2025 23:12:38 +0100
|
||||
Subject: [PATCH] avoid extra memset when clearing an empty table
|
||||
|
||||
---
|
||||
include/ankerl/unordered_dense.h | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h
|
||||
index 0835342..4938212 100644
|
||||
--- a/include/ankerl/unordered_dense.h
|
||||
+++ b/include/ankerl/unordered_dense.h
|
||||
@@ -1490,8 +1490,10 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
|
||||
// modifiers //////////////////////////////////////////////////////////////
|
||||
|
||||
void clear() {
|
||||
- m_values.clear();
|
||||
- clear_buckets();
|
||||
+ if (!empty()) {
|
||||
+ m_values.clear();
|
||||
+ clear_buckets();
|
||||
+ }
|
||||
}
|
||||
|
||||
auto insert(value_type const& value) -> std::pair<iterator, bool> {
|
||||
|
|
@ -576,6 +576,7 @@ add_subdirectory(externals)
|
|||
# pass targets from externals
|
||||
find_package(enet)
|
||||
find_package(MbedTLS)
|
||||
find_package(unordered_dense REQUIRED)
|
||||
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
find_package(xbyak)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ if (NOT TARGET LLVM::Demangle)
|
|||
add_library(LLVM::Demangle ALIAS demangle)
|
||||
endif()
|
||||
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
if (YUZU_STATIC_ROOM)
|
||||
return()
|
||||
endif()
|
||||
|
|
@ -244,9 +247,6 @@ if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
|
|||
AddJsonPackage(cpp-jwt)
|
||||
endif()
|
||||
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
# FFMpeg
|
||||
if (YUZU_USE_EXTERNAL_FFMPEG OR YUZU_USE_BUNDLED_FFMPEG)
|
||||
add_subdirectory(ffmpeg)
|
||||
|
|
|
|||
|
|
@ -91,10 +91,13 @@
|
|||
"unordered-dense": {
|
||||
"package": "unordered_dense",
|
||||
"repo": "martinus/unordered_dense",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b98b5d4d96f8e0081b184d6c4c1181fae4e41723b54bed4296717d7f417348b48fad0bbcc664cac142b8c8a47e95aa57c1eb1cf6caa855fd782fad3e3ab99e5e",
|
||||
"sha": "7b55cab841",
|
||||
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||
"find_args": "CONFIG",
|
||||
"git_version": "4.8.1"
|
||||
"bundled": true,
|
||||
"patches": [
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
]
|
||||
},
|
||||
"mbedtls": {
|
||||
"package": "MbedTLS",
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||
SHOW_SHADERS_BUILDING("show_shaders_building"),
|
||||
|
||||
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||
USE_LRU_CACHE("use_lru_cache"),
|
||||
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
|
||||
ENABLE_OVERLAY("enable_overlay"),
|
||||
|
||||
|
|
|
|||
|
|
@ -123,13 +123,6 @@ abstract class SettingsItem(
|
|||
// List of all general
|
||||
val settingsItems = HashMap<String, SettingsItem>().apply {
|
||||
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_LRU_CACHE,
|
||||
titleId = R.string.use_lru_cache,
|
||||
descriptionId = R.string.use_lru_cache_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
|
||||
|
|
|
|||
|
|
@ -235,7 +235,6 @@ class SettingsFragmentPresenter(
|
|||
|
||||
add(HeaderSetting(R.string.cpu))
|
||||
add(IntSetting.FAST_CPU_TIME.key)
|
||||
add(BooleanSetting.USE_LRU_CACHE.key)
|
||||
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
|
||||
|
||||
add(IntSetting.MEMORY_LAYOUT.key)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
|
@ -110,7 +110,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
|||
val commonVars = listOf(
|
||||
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
|
||||
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
|
||||
"TU_BREADCRUMBS"
|
||||
"TU_BREADCRUMBS", "FD_DEV_FEATURES"
|
||||
)
|
||||
|
||||
for (varName in commonVars) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
|
@ -144,6 +144,15 @@ object FreedrenoPresets {
|
|||
)
|
||||
)
|
||||
|
||||
val DEV_FEATURES_UBWC_HINT = FreedrenoPreset(
|
||||
name = "Dev - UBWC Flag Hint",
|
||||
description = "Enable TP UBWC flag hint for development",
|
||||
icon = "ic_dev_features",
|
||||
variables = mapOf(
|
||||
"FD_DEV_FEATURES" to "enable_tp_ubwc_flag_hint=1"
|
||||
)
|
||||
)
|
||||
|
||||
val PERFORMANCE_DEFAULT = FreedrenoPreset(
|
||||
name = "Performance - Default",
|
||||
description = "Clear all debug options for performance",
|
||||
|
|
@ -159,6 +168,7 @@ object FreedrenoPresets {
|
|||
CAPTURE_FRAMES,
|
||||
SHADER_DEBUG,
|
||||
GPU_HANG_TRACE,
|
||||
DEV_FEATURES_UBWC_HINT,
|
||||
PERFORMANCE_DEFAULT
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,10 +54,6 @@ namespace AndroidSettings {
|
|||
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
|
||||
Settings::Category::GpuDriver};
|
||||
|
||||
// LRU Cache
|
||||
Settings::SwitchableSetting<bool> use_lru_cache{linkage, true, "use_lru_cache",
|
||||
Settings::Category::System};
|
||||
|
||||
Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
|
||||
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
|
||||
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -18,7 +21,7 @@ struct RomMetadata {
|
|||
bool isHomebrew;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
|
||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
const auto file =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/**
|
||||
|
|
@ -258,6 +258,11 @@ Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoEnv(
|
|||
}
|
||||
|
||||
LOG_INFO(Frontend, "[Freedreno] Set {}={}", var_name, value);
|
||||
|
||||
if (var_name == "FD_DEV_FEATURES") {
|
||||
LOG_INFO(Frontend, "[Freedreno] FD_DEV_FEATURES enabled: {}", value);
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -18,7 +21,7 @@
|
|||
#include "input_common/drivers/virtual_gamepad.h"
|
||||
#include "native.h"
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
ankerl::unordered_dense::map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
|
||||
bool IsHandheldOnly() {
|
||||
const auto npad_style_set =
|
||||
|
|
|
|||
|
|
@ -94,9 +94,6 @@
|
|||
|
||||
<string name="use_sync_core">مزامنة سرعة النواة</string>
|
||||
<string name="use_sync_core_description">قم بمزامنة سرعة النواة مع النسبة المئوية للسرعة القصوى لتحسين الأداء دون تغيير السرعة الفعلية للعبة.</string>
|
||||
<string name="use_lru_cache">تمكين ذاكرة التخزين المؤقتة LRU</string>
|
||||
<string name="use_lru_cache_description">قم بتمكين أو تعطيل ذاكرة التخزين المؤقتة الأقل استخدامًا (LRU)، مما يزيد من الأداء عن طريق توفير استخدام معالج وحدة المعالجة المركزية. قد تواجه بعض الألعاب مشكلات مع هذا الإعداد، لذا قم بتعطيله إذا لم يتم تشغيل اللعبة أو تعطلت بشكل عشوائي.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">تمكين محاكاة MMU المضيف</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</string>
|
||||
<string name="debug_knobs">مقابض تصحيح الأخطاء</string>
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">مزامنة سرعة النواة</string>
|
||||
<string name="use_sync_core_description">خێرایی تیکەکانی ناوک ڕێکبخە لەگەڵ ڕێژەی خێرایی بەرزترین بۆ باشترکردنی کارایی بەبێ گۆڕینی خێرایی ڕاستەقینەی یارییەکە.</string>
|
||||
<string name="use_lru_cache">تمكين ذاكرة التخزين المؤقت LRU</string>
|
||||
<string name="use_lru_cache_description">چالاک یان ناچالاککردنی کاشەی LRU، کارایی باشتر دەکات بە هەڵگرتنی بەکارهێنانی پرۆسەی CPU. هەندێک یاری کێشەی لەگەڵ هەیە، بەتایبەتی TotK 1.2.1، بۆیە بیخەوێنە ئەگەر یاریەکە نەگەڕێت یان بە هەڕەمەکی بشکێت.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">چالاککردنی میمیکردنی MMU میواندە</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">ئەم باشکردنە خێرایی دەستکەوتنی بیرگە لەلایەن پرۆگرامی میوانەکە زیاد دەکات. چالاککردنی وای لێدەکات کە خوێندنەوە/نووسینەکانی بیرگەی میوانەکە ڕاستەوخۆ لە بیرگە ئەنجام بدرێت و میمیکردنی MMU میواندە بەکاربهێنێت. ناچالاککردنی ئەمە هەموو دەستکەوتنەکانی بیرگە ڕەت دەکاتەوە لە بەکارهێنانی میمیکردنی MMU نەرمەکاڵا.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@
|
|||
|
||||
<string name="use_sync_core">Synchronizovat rychlost jádra</string>
|
||||
<string name="use_sync_core_description">Synchronizuje rychlost jádra s nastaveným limitem rychlosti, aby se zlepšil výkon bez zrychlení samotné hry.</string>
|
||||
<string name="use_lru_cache">Zapnout mezipaměť LRU</string>
|
||||
<string name="use_lru_cache_description">Zapne, nebo vypne mezipaměť LRU (Least Recently Used). Ta zvyšuje výkon tím, že šetří využití procesoru. Některé hry s tímto mohou mít problém, takže pokud se hra nespustí, nebo náhodně padá, tuto volbu vypněte.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Povolit emulaci hostitelské MMU</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Tato optimalizace zrychluje hostovanému programu přístupy do paměti. Zapnutím této funkce se čtení i zápisy do paměti provádějí přímo a využívají hostitelskou MMU. Při vypnutí musí všechny přístupy do paměti použít softwarovou emulaci MMU. </string>
|
||||
<string name="debug_knobs">Ladící parametry</string>
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@
|
|||
|
||||
<string name="use_sync_core">Kern-Geschwindigkeit synchronisieren</string>
|
||||
<string name="use_sync_core_description">Synchronisiert die Taktrate des Kerns mit der maximalen Geschwindigkeit, um die Leistung zu verbessern, ohne die tatsächliche Spielgeschwindigkeit zu verändern.</string>
|
||||
<string name="use_lru_cache">LRU-Cache aktivieren</string>
|
||||
<string name="use_lru_cache_description">Aktivieren oder deaktivieren Sie den LRU-Cache, um die Leistung durch Einsparung von CPU-Prozessorauslastung zu verbessern. Einige Spiele haben Probleme damit, insbesondere TotK 1.2.1, deaktivieren Sie es also, wenn das Spiel nicht startet oder zufällig abstürzt.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Host-MMU-Emulation aktivieren</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Diese Optimierung beschleunigt Speicherzugriffe durch das Gastprogramm. Wenn aktiviert, erfolgen Speicherlese- und -schreibvorgänge des Gastes direkt im Speicher und nutzen die MMU des Hosts. Das Deaktivieren erzwingt die Verwendung der Software-MMU-Emulation für alle Speicherzugriffe.</string>
|
||||
<string name="debug_knobs">Debug-Regler</string>
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@
|
|||
|
||||
<string name="use_sync_core">Sincronizar la velocidad del núcleo</string>
|
||||
<string name="use_sync_core_description">Sincroniza la velocidad del núcleo con el porcentaje máximo de velocidad para mejorar el rendimiento sin alterar la velocidad real del juego.</string>
|
||||
<string name="use_lru_cache">Habilitar caché LRU</string>
|
||||
<string name="use_lru_cache_description">Habilite o deshabilite la caché menos utilizada recientemente (LRU), aumentando el rendimiento al ahorrar el uso del proceso de la CPU. Algunos juegos pueden ver problemas con esta configuración, así que desactívela si el juego no arranca o se bloquea aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Habilitar emulación de MMU del anfitrión</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta optimización acelera el acceso a la memoria del programa invitado. Al habilitarla, las lecturas y escrituras de la memoria del invitado se realizan directamente en la memoria y utilizan la MMU del anfitrión. Al deshabilitarla, todos los accesos a la memoria utilizan el software de emulación de la MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@
|
|||
<string name="sync_memory_operations_description">اطمینان از سازگاری دادهها بین عملیات محاسباتی و حافظه. این گزینه ممکن است مشکلات برخی بازیها را رفع کند، اما در برخی موارد ممکن است عملکرد را کاهش دهد. به نظر میرسد بازیهای با Unreal Engine 4 بیشترین تأثیر را داشته باشند.</string>
|
||||
<string name="buffer_reorder_disable">غیرفعال کردن مرتبسازی مجدد بافر</string>
|
||||
<string name="buffer_reorder_disable_description">در صورت انتخاب، مرتبسازی مجدد آپلودهای حافظه نگاشتشده غیرفعال میشود که امکان ارتباط آپلودها با ترسیمات خاص را فراهم میکند. ممکن است در برخی موارد عملکرد را کاهش دهد.</string>
|
||||
<string name="use_lru_cache">فعالسازی حافظه نهان LRU</string>
|
||||
<string name="use_lru_cache_description">حافظه پنهان LRU را فعال یا غیرفعال کنید تا با کاهش استفاده از پردازنده، عملکرد بهبود یابد. برخی بازیها مانند TotK 1.2.1 با این ویژگی مشکل دارند، در صورت عدم راهاندازی یا قطعی تصادفی بازی، آن را غیرفعال کنید.</string>
|
||||
<string name="dyna_state">حالت پویای گسترده</string>
|
||||
<string name="dyna_state_description">تعداد قابلیتهایی که میتوان در حالت Extended Dynamic State استفاده کرد را کنترل میکند. اعداد بالاتر قابلیتهای بیشتری را فعال کرده و میتوانند عملکرد را افزایش دهند، اما ممکن است با برخی درایورها و فروشندگان مشکلاتی ایجاد کنند. مقدار پیشفرض بسته به سیستم و قابلیتهای سختافزاری شما ممکن است متفاوت باشد. این مقدار را میتوان تغییر داد تا زمانی که پایداری و کیفیت بصری بهتری حاصل شود.</string>
|
||||
<string name="disabled">غیرفعال</string>
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@
|
|||
|
||||
<string name="use_sync_core">Synchroniser la vitesse du cœur</string>
|
||||
<string name="use_sync_core_description">Synchronise la vitesse du cœur avec le pourcentage de vitesse maximal pour améliorer les performances sans modifier la vitesse réelle du jeu.</string>
|
||||
<string name="use_lru_cache">Activer le cache LRU</string>
|
||||
<string name="use_lru_cache_description">Active ou désactive le cache LRU (Least Recently Used) pour améliorer les performances en réduisant l’utilisation du processeur. Certains jeux peuvent rencontrer des problèmes avec ce réglage ; désactivez-le si le jeu ne démarre pas ou plante de manière aléatoire.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Activer l\'émulation de la MMU hôte</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Cette optimisation accélère les accès mémoire par le programme invité. L\'activer entraîne que les lectures/écritures mémoire de l\'invité sont effectuées directement en mémoire et utilisent la MMU de l\'hôte. Désactiver cela force tous les accès mémoire à utiliser l\'émulation logicielle de la MMU.</string>
|
||||
<string name="debug_knobs">Boutons de débogage</string>
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@
|
|||
|
||||
<string name="use_sync_core">סנכרון מהירות ליבה</string>
|
||||
<string name="use_sync_core_description">סנכרן את מהירות הליבה לאחוז המהירות המרבי כדי לשפר ביצועים מבלי לשנות את מהירות המשחק בפועל.</string>
|
||||
<string name="use_lru_cache">הפעלת מטמון LRU</string>
|
||||
<string name="use_lru_cache_description">הפעל או השבת מטמון LRU לשיפור ביצועים על ידי חיסכון בשימוש במעבד. לחלק מהמשחקים כמו TotK 1.2.1 יש בעיות - השבת אם המשחק לא עולה או קורס באקראי.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">הפעל אמולציית MMU מארח</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">אופטימיזציה זו מאיצה את גישת הזיכרון על ידי התוכנית האורחת. הפעלתה גורמת לכך שפעולות קריאה/כתיבה לזיכרון האורח מתבצעות ישירות לזיכרון ומשתמשות ב-MMU של המארח. השבתת זאת מאלצת את כל גישות הזיכרון להשתמש באמולציית MMU תוכנתית.</string>
|
||||
<string name="debug_knobs_description">לשימוש בפיתוח בלבד.</string>
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">Magsebesség szinkronizálása</string>
|
||||
<string name="use_sync_core_description">A mag sebességének szinkronizálása a maximális sebesség százalékával a teljesítmény javítása érdekében a játék tényleges sebességének megváltoztatása nélkül.</string>
|
||||
<string name="use_lru_cache">LRU gyorsítótár engedélyezése</string>
|
||||
<string name="use_lru_cache_description">Engedélyezi vagy letiltja az LRU gyorsítótárat, növelve a teljesítményt a CPU használat csökkentésével. Néhány játéknak (különösen a TotK 1.2.1-nek) problémája lehet vele - tiltsa le, ha a játék nem indul el vagy véletlenszerűen összeomlik.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Gazda MMU emuláció engedélyezése</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ez az optimalizáció gyorsítja a vendégprogram memória-hozzáférését. Engedélyezése esetén a vendég memóriaolvasási/írási műveletei közvetlenül a memóriában történnek, és kihasználják a gazda MMU-ját. Letiltás esetén minden memória-hozzáférés a szoftveres MMU emulációt használja.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -77,9 +77,6 @@
|
|||
|
||||
<string name="use_sync_core">Sinkronisasi Kecepatan Inti</string>
|
||||
<string name="use_sync_core_description">Sinkronkan kecepatan inti dengan persentase kecepatan maksimum untuk meningkatkan performa tanpa mengubah kecepatan sebenarnya dari permainan.</string>
|
||||
<string name="use_lru_cache">Aktifkan LRU Cache</string>
|
||||
<string name="use_lru_cache_description">Aktifkan atau nonaktifkan cache LRU untuk meningkatkan performa dengan mengurangi penggunaan proses CPU. Beberapa game seperti TotK 1.2.1 memiliki masalah - nonaktifkan jika game tidak mau berjalan atau crash acak.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Aktifkan Emulasi MMU Host</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Optimasi ini mempercepat akses memori oleh program tamu. Mengaktifkannya menyebabkan pembacaan/penulisan memori tamu dilakukan langsung ke memori dan memanfaatkan MMU Host. Menonaktifkan ini memaksa semua akses memori menggunakan Emulasi MMU Perangkat Lunak.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@
|
|||
|
||||
<string name="use_sync_core">Sincronizza velocità core</string>
|
||||
<string name="use_sync_core_description">Sincronizza la velocità del core con la percentuale massima di velocità per migliorare le prestazioni senza alterare la velocità effettiva del gioco.</string>
|
||||
<string name="use_lru_cache">Abilita cache LRU</string>
|
||||
<string name="use_lru_cache_description">Abilita o disabilita la cache LRU per migliorare le prestazioni riducendo l\'uso della CPU. Alcuni giochi come TotK 1.2.1 hanno problemi - disabilitalo se il gioco non si avvia o crasha casualmente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Abilita l\'emulazione della MMU nell\'host</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Questa ottimizzazione accelera gli accessi alla memoria da parte del programma guest. Abilitandola, le letture/scritture della memoria guest vengono eseguite direttamente in memoria e sfruttano la MMU host. Disabilitandola, tutti gli accessi alla memoria sono costretti a utilizzare l\'emulazione software della MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">コア速度の同期</string>
|
||||
<string name="use_sync_core_description">コアの速度を最大速度パーセンテージに同期させ、ゲームの実際の速度を変えずにパフォーマンスを向上させます。</string>
|
||||
<string name="use_lru_cache">LRUキャッシュを有効化</string>
|
||||
<string name="use_lru_cache_description">LRUキャッシュを有効/無効にし、CPUプロセスの使用を節約してパフォーマンスを向上させます。TotK 1.2.1など一部のゲームで問題が発生する可能性があるため、ゲームが起動しない場合やランダムにクラッシュする場合は無効にしてください。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">ホストMMUエミュレーションを有効化</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">この最適化により、ゲストプログラムによるメモリアクセスが高速化されます。有効にすると、ゲストのメモリ読み書きが直接メモリ内で実行され、ホストのMMUを利用します。無効にすると、すべてのメモリアクセスでソフトウェアMMUエミュレーションが使用されます。</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">코어 속도 동기화</string>
|
||||
<string name="use_sync_core_description">코어 틱 속도를 최대 속도 백분율과 동기화하여 게임의 실제 속도를 변경하지 않고 성능을 향상시킵니다.</string>
|
||||
<string name="use_lru_cache">LRU 캐시 사용</string>
|
||||
<string name="use_lru_cache_description">LRU 캐시를 활성화 또는 비활성화하여 CPU 프로세스 사용을 절약하고 성능을 향상시킵니다. TotK 1.2.1을 포함한 일부 게임에서 문제가 발생할 수 있으므로 게임이 부팅되지 않거나 무작위로 충돌하는 경우 비활성화하세요.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">호스트 MMU 에뮬레이션 사용</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">이 최적화는 게스트 프로그램의 메모리 접근 속도를 높입니다. 활성화하면 게스트의 메모리 읽기/쓰기가 메모리에서 직접 수행되고 호스트의 MMU를 활용합니다. 비활성화하면 모든 메모리 접근에 소프트웨어 MMU 에뮬레이션을 사용하게 됩니다.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">Synkroniser kjernespeed</string>
|
||||
<string name="use_sync_core_description">Synkroniser kjernens hastighet med maksimal hastighetsprosent for å forbedre ytelsen uten å endre spillets faktiske hastighet.</string>
|
||||
<string name="use_lru_cache">Aktiver LRU-mellomlager</string>
|
||||
<string name="use_lru_cache_description">Aktiver eller deaktiver LRU-mellomlager for å forbedre ytelsen ved å spare CPU-prosessorbruk. Noen spill som TotK 1.2.1 har problemer med dette - deaktiver hvis spillet ikke starter eller krasjer tilfeldig.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Aktiver verts-MMU-emulering</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Denne optimaliseringen fremskynder minnetilgang av gjesteprogrammet. Hvis aktivert, utføres gjestens minnelesing/skriving direkte i minnet og bruker vertens MMU. Deaktivering tvinger alle minnetilganger til å bruke programvarebasert MMU-emulering.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@
|
|||
|
||||
<string name="use_sync_core">Synchronizuj prędkość rdzenia</string>
|
||||
<string name="use_sync_core_description">Synchronizuje prędkość rdzenia z maksymalnym procentem prędkości, aby poprawić wydajność bez zmiany rzeczywistej prędkości gry.</string>
|
||||
<string name="use_lru_cache">Włącz pamięć podręczną LRU</string>
|
||||
<string name="use_lru_cache_description">Włącz lub wyłącz pamięć podręczną LRU, aby poprawić wydajność poprzez zmniejszenie użycia procesora. Niektóre gry, takie jak TotK 1.2.1, mogą mieć problemy - wyłącz, jeśli gra się nie uruchamia lub losowo zawiesza.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Włącz emulację MMU hosta</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ta optymalizacja przyspiesza dostęp do pamięci przez program gościa. Włączenie powoduje, że odczyty/zapisy pamięci gościa są wykonywane bezpośrednio w pamięci i wykorzystują MMU hosta. Wyłączenie wymusza użycie programowej emulacji MMU dla wszystkich dostępów do pamięci.</string>
|
||||
<string name="debug_knobs">Przełączniki debugowania</string>
|
||||
|
|
|
|||
|
|
@ -84,9 +84,6 @@
|
|||
|
||||
<string name="use_sync_core">Sincronizar Velocidade do Núcleo</string>
|
||||
<string name="use_sync_core_description">Aumenta a velocidade do ciclo do núcleo até o máximo para melhorar o desempenho, mantendo a velocidade real do jogo inalterada.</string>
|
||||
<string name="use_lru_cache">Ativar Cache LRU</string>
|
||||
<string name="use_lru_cache_description">O cache Least Recently Used (LRU, ou Itens Menos Usados) aumenta o desempenho, economizando o uso do processador. Alguns jogos podem apresentar problemas com esta configuração. Desative-a caso o jogo não inicie ou trave aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação da MMU do Sistema</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera o acesso à memória pelo programa. Ao ativá-la, leituras e gravações são feitas diretamente na memória, usando a MMU (Unidade de Gerenciamento de Memória) do sistema. Ao desativar, todos os acessos passam a utilizar a Emulação de MMU por Software.</string>
|
||||
<string name="debug_knobs">Controles de Depuração</string>
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">Sincronizar velocidade do núcleo</string>
|
||||
<string name="use_sync_core_description">Sincroniza a velocidade do núcleo com a percentagem máxima de velocidade para melhorar o desempenho sem alterar a velocidade real do jogo.</string>
|
||||
<string name="use_lru_cache">Ativar cache LRU</string>
|
||||
<string name="use_lru_cache_description">Ative ou desative a cache LRU para melhorar desempenho poupando uso do processador. Alguns jogos como TotK 1.2.1 têm problemas - desative se o jogo não iniciar ou falhar aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação de MMU do Anfitrião</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera os acessos à memória pelo programa convidado. Ativar faz com que as leituras/escritas de memória do convidado sejam efetuadas diretamente na memória e utilizem a MMU do Anfitrião. Desativar força todos os acessos à memória a usar a Emulação de MMU por Software.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -92,9 +92,6 @@
|
|||
|
||||
<string name="use_sync_core">Синхронизация скорости ядра</string>
|
||||
<string name="use_sync_core_description">Синхронизирует скорость ядра с максимальным процентом скорости для улучшения производительности без изменения фактической скорости игры.</string>
|
||||
<string name="use_lru_cache">Включить LRU-кеш</string>
|
||||
<string name="use_lru_cache_description">Включите или отключите кэш LRU (наименее недавно использованный), повышая производительность за счёт снижения нагрузки на ЦП. Некоторые игры могут работать с ним некорректно (например, TotK 1.2.1), поэтому отключите, если игра не запускается или случайно вылетает.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Включить эмуляцию MMU хоста</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Эта оптимизация ускоряет доступ к памяти гостевой программой. При включении операции чтения/записи памяти гостя выполняются напрямую в памяти с использованием MMU хоста. Отключение заставляет все обращения к памяти использовать программную эмуляцию MMU.</string>
|
||||
<string name="debug_knobs">Настройки отладки</string>
|
||||
|
|
|
|||
|
|
@ -56,9 +56,6 @@
|
|||
|
||||
<string name="use_sync_core">Синхронизујте брзину језгре</string>
|
||||
<string name="use_sync_core_description">Синхронизујте језгро Тицк Брзина брзине максималне брзине да бисте побољшали перформансе без измене стварне брзине игре.</string>
|
||||
<string name="use_lru_cache">Омогући ЛРУ кеш</string>
|
||||
<string name="use_lru_cache_description">Омогућите или онемогућите најмање недавно коришћене (ЛРУ) кеш меморије, све веће перформансе штедећи употребу процеса ЦПУ-а. Неке игре са њом имају проблеме, посебно тотк 1.2.1, тако да онемогућите ако се игра не покрене или се руши насумично.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Омогући емулацију MMU домаћина</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ова оптимизација убрзава приступ меморији од стране гостујућег програма. Укључивање изазива да се читања/уписа меморије госта обављају директно у меморији и користе MMU домаћина. Искључивање присиљава све приступе меморији да користе софтверску емулацију MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -94,9 +94,6 @@
|
|||
|
||||
<string name="use_sync_core">Синхронізувати швидкість ядра</string>
|
||||
<string name="use_sync_core_description">Синхронізує швидкість ядра з максимальним відсотком швидкості для покращення продуктивності без зміни реальної швидкості гри.</string>
|
||||
<string name="use_lru_cache">Увімкнути LRU-кеш</string>
|
||||
<string name="use_lru_cache_description">Увімкніть або вимкніть кеш LRU (Least Recently Used) для покращення продуктивності шляхом зменшення навантаження на CPU. Деякі ігри (зокрема TotK 1.2.1) можуть працювати некоректно - вимкніть, якщо гра не запускається або раптово вилітає.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Увімкнути емуляцію MMU хоста</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ця оптимізація пришвидшує доступ до пам\'яті гостьовою програмою. Увімкнення призводить до того, що читання/запис пам\'яті гостя виконуються безпосередньо в пам\'яті та використовують MMU хоста. Вимкнення змушує всі звернення до пам\'яті використовувати програмну емуляцію MMU.</string>
|
||||
<string name="debug_knobs">Зневаджувальні регулятори</string>
|
||||
|
|
|
|||
|
|
@ -58,9 +58,6 @@
|
|||
|
||||
<string name="use_sync_core">Đồng bộ tốc độ lõi</string>
|
||||
<string name="use_sync_core_description">Đồng bộ tốc độ lõi với tỷ lệ phần trăm tốc độ tối đa để cải thiện hiệu suất mà không làm thay đổi tốc độ thực tế của trò chơi.</string>
|
||||
<string name="use_lru_cache">Bật bộ nhớ đệm LRU</string>
|
||||
<string name="use_lru_cache_description">Bật hoặc tắt bộ nhớ đệm LRU để cải thiện hiệu suất bằng cách tiết kiệm quy trình sử dụng CPU. Một số trò chơi như TotK 1.2.1 có vấn đề - hãy tắt nếu trò chơi không khởi động hoặc bị treo ngẫu nhiên.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Bật giả lập MMU Máy chủ</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Tối ưu hóa này tăng tốc độ truy cập bộ nhớ của chương trình khách. Bật nó lên khiến các thao tác đọc/ghi bộ nhớ khách được thực hiện trực tiếp vào bộ nhớ và sử dụng MMU của Máy chủ. Tắt tính năng này buộc tất cả quyền truy cập bộ nhớ phải sử dụng Giả lập MMU Phần mềm.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
|
|
|
|||
|
|
@ -94,9 +94,6 @@
|
|||
|
||||
<string name="use_sync_core">同步核心速度</string>
|
||||
<string name="use_sync_core_description">将核心速度与最大速度百分比同步,在不改变游戏实际速度的情况下提高性能。</string>
|
||||
<string name="use_lru_cache">启用LRU缓存</string>
|
||||
<string name="use_lru_cache_description">启用或禁用LRU缓存,通过节省CPU进程使用来提高性能。某些游戏可能存在问题,特别是TotK 1.2.1,如果游戏无法启动或随机崩溃,请禁用此选项。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">启用主机 MMU 模拟</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。</string>
|
||||
<string name="debug_knobs">调试开关</string>
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@
|
|||
|
||||
<string name="use_sync_core">同步核心速度</string>
|
||||
<string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能。</string>
|
||||
<string name="use_lru_cache">啟用LRU快取</string>
|
||||
<string name="use_lru_cache_description">啟用或停用LRU快取,透過節省CPU進程使用率來提高效能。某些遊戲可能存在問題,特別是薩爾達傳說:王國之淚 v1.2.1(更新),如果遊戲無法啟動或隨機崩潰,請停用此選項。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">啟用主機 MMU 模擬</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速來賓程式的記憶體存取。啟用後,來賓記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。</string>
|
||||
<string name="debug_knobs">偵錯開關</string>
|
||||
|
|
|
|||
|
|
@ -99,9 +99,6 @@
|
|||
|
||||
<string name="use_sync_core">Synchronize Core Speed</string>
|
||||
<string name="use_sync_core_description">Synchronize the core tick speed to the maximum speed percentage to improve performance without altering the game\'s actual speed.</string>
|
||||
<string name="use_lru_cache">Enable LRU Cache</string>
|
||||
<string name="use_lru_cache_description">Enable or disable the Least Recently Used (LRU) cache, increasing performance by saving CPU process usage. Some games may see issues with this setting, so disable it if the game doesn\'t boot or crashes randomly.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Enable Host MMU Emulation</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">This optimization speeds up memory accesses by the guest program. Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host\'s MMU. Disabling this forces all memory accesses to use Software MMU Emulation.</string>
|
||||
<string name="debug_knobs">Debug knobs</string>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-License-Identifier:
|
||||
|
|
@ -186,8 +186,6 @@ if(ARCHITECTURE_x86_64)
|
|||
common
|
||||
PRIVATE x64/cpu_detect.cpp
|
||||
x64/cpu_detect.h
|
||||
x64/cpu_wait.cpp
|
||||
x64/cpu_wait.h
|
||||
x64/native_clock.cpp
|
||||
x64/native_clock.h
|
||||
x64/rdtsc.cpp
|
||||
|
|
@ -255,7 +253,7 @@ if (lz4_ADDED)
|
|||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense)
|
||||
target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd)
|
||||
|
||||
if(ANDROID)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
|
|
@ -193,8 +193,8 @@ private:
|
|||
SetLegacyPathImpl(legacy_path, new_path);
|
||||
}
|
||||
|
||||
std::unordered_map<EdenPath, fs::path> eden_paths;
|
||||
std::unordered_map<EmuPath, fs::path> legacy_paths;
|
||||
ankerl::unordered_dense::map<EdenPath, fs::path> eden_paths;
|
||||
ankerl::unordered_dense::map<EmuPath, fs::path> legacy_paths;
|
||||
};
|
||||
|
||||
bool ValidatePath(const fs::path& path) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#ifdef _WIN32
|
||||
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/icl/separate_interval_set.hpp>
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
|
|
@ -391,7 +391,7 @@ private:
|
|||
|
||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/logging/log.h"
|
||||
|
|
@ -409,7 +412,7 @@ public:
|
|||
namespace Impl {
|
||||
|
||||
template <typename InputDeviceType>
|
||||
using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||
using FactoryListType = ankerl::unordered_dense::map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||
|
||||
template <typename InputDeviceType>
|
||||
struct FactoryList {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -5,14 +8,14 @@
|
|||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// A string-based key-value container supporting serializing to and deserializing from a string
|
||||
class ParamPackage {
|
||||
public:
|
||||
using DataType = std::unordered_map<std::string, std::string>;
|
||||
using DataType = ankerl::unordered_dense::map<std::string, std::string>;
|
||||
|
||||
ParamPackage() = default;
|
||||
explicit ParamPackage(const std::string& serialized);
|
||||
|
|
|
|||
|
|
@ -217,11 +217,6 @@ struct Values {
|
|||
SwitchableSetting<bool> sync_core_speed{linkage, false, "sync_core_speed", Category::Core,
|
||||
Specialization::Default};
|
||||
|
||||
// Memory
|
||||
#ifdef HAS_NCE
|
||||
SwitchableSetting<bool> lru_cache_enabled{linkage, false, "use_lru_cache", Category::System};
|
||||
#endif
|
||||
|
||||
// Cpu
|
||||
SwitchableSetting<CpuBackend, true> cpu_backend{linkage,
|
||||
#ifdef HAS_NCE
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#include "common/x64/cpu_wait.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
namespace {
|
||||
|
||||
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
|
||||
// For reference:
|
||||
// At 1 GHz, 100K cycles is 100us
|
||||
// At 2 GHz, 100K cycles is 50us
|
||||
// At 4 GHz, 100K cycles is 25us
|
||||
constexpr auto PauseCycles = 100'000U;
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
__forceinline static void TPAUSE() {
|
||||
static constexpr auto RequestC02State = 0U;
|
||||
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
|
||||
}
|
||||
|
||||
__forceinline static void MWAITX() {
|
||||
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||
static constexpr auto RequestC1State = 0U;
|
||||
|
||||
// monitor_var should be aligned to a cache line.
|
||||
alignas(64) u64 monitor_var{};
|
||||
_mm_monitorx(&monitor_var, 0, 0);
|
||||
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
|
||||
}
|
||||
#else
|
||||
static void TPAUSE() {
|
||||
static constexpr auto RequestC02State = 0U;
|
||||
const auto tsc = FencedRDTSC() + PauseCycles;
|
||||
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
|
||||
const auto edx = static_cast<u32>(tsc >> 32);
|
||||
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
|
||||
}
|
||||
|
||||
static void MWAITX() {
|
||||
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||
static constexpr auto RequestC1State = 0U;
|
||||
|
||||
// monitor_var should be aligned to a cache line.
|
||||
alignas(64) u64 monitor_var{};
|
||||
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
|
||||
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
|
||||
}
|
||||
#endif
|
||||
|
||||
void MicroSleep() {
|
||||
static const bool has_waitpkg = GetCPUCaps().waitpkg;
|
||||
static const bool has_monitorx = GetCPUCaps().monitorx;
|
||||
|
||||
if (has_waitpkg) {
|
||||
TPAUSE();
|
||||
} else if (has_monitorx) {
|
||||
MWAITX();
|
||||
} else {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
void MicroSleep();
|
||||
|
||||
} // namespace Common::X64
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include "common/common_types.h"
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
class LRUCache {
|
||||
public:
|
||||
using key_type = KeyType;
|
||||
using value_type = ValueType;
|
||||
using size_type = std::size_t;
|
||||
|
||||
struct Statistics {
|
||||
size_type hits = 0;
|
||||
size_type misses = 0;
|
||||
void reset() noexcept { hits = misses = 0; }
|
||||
};
|
||||
|
||||
explicit LRUCache(size_type capacity, bool enabled = true)
|
||||
: enabled_{enabled}, capacity_{capacity} {
|
||||
cache_map_.reserve(capacity_);
|
||||
LOG_WARNING(Core, "LRU Cache initialised (state: {} | capacity: {})", enabled_ ? "enabled" : "disabled", capacity_);
|
||||
}
|
||||
|
||||
// Non-movable copy semantics
|
||||
LRUCache(const LRUCache&) = delete;
|
||||
LRUCache& operator=(const LRUCache&) = delete;
|
||||
LRUCache(LRUCache&& other) noexcept { *this = std::move(other); }
|
||||
LRUCache& operator=(LRUCache&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
std::unique_lock this_lock(mutex_, std::defer_lock);
|
||||
std::unique_lock other_lock(other.mutex_, std::defer_lock);
|
||||
std::lock(this_lock, other_lock);
|
||||
enabled_ = other.enabled_;
|
||||
capacity_ = other.capacity_;
|
||||
cache_list_ = std::move(other.cache_list_);
|
||||
cache_map_ = std::move(other.cache_map_);
|
||||
stats_ = other.stats_;
|
||||
return *this;
|
||||
}
|
||||
~LRUCache() = default;
|
||||
|
||||
[[nodiscard]] value_type* get(const key_type& key) {
|
||||
if (!enabled_) [[unlikely]] return nullptr;
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it == cache_map_.end()) {
|
||||
++stats_.misses;
|
||||
return nullptr;
|
||||
}
|
||||
move_to_front(it);
|
||||
++stats_.hits;
|
||||
return &it->second.second;
|
||||
}
|
||||
|
||||
[[nodiscard]] value_type* peek(const key_type& key) const {
|
||||
if (!enabled_) [[unlikely]] return nullptr;
|
||||
std::shared_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
return it == cache_map_.end() ? nullptr : &it->second.second;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void put(const key_type& key, V&& value) {
|
||||
if (!enabled_) [[unlikely]] return;
|
||||
std::unique_lock lock(mutex_);
|
||||
insert_or_update(key, std::forward<V>(value));
|
||||
}
|
||||
|
||||
template <typename ValueFactory>
|
||||
value_type& get_or_emplace(const key_type& key, ValueFactory&& factory) {
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it != cache_map_.end()) {
|
||||
move_to_front(it);
|
||||
return it->second.second;
|
||||
}
|
||||
value_type new_value = factory();
|
||||
insert_or_update(key, std::move(new_value));
|
||||
return cache_map_.find(key)->second.second;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const key_type& key) const {
|
||||
if (!enabled_) return false;
|
||||
std::shared_lock lock(mutex_);
|
||||
return cache_map_.find(key) != cache_map_.end();
|
||||
}
|
||||
|
||||
bool erase(const key_type& key) {
|
||||
if (!enabled_) return false;
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = cache_map_.find(key);
|
||||
if (it == cache_map_.end()) return false;
|
||||
cache_list_.erase(it->second.first);
|
||||
cache_map_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::unique_lock lock(mutex_);
|
||||
cache_list_.clear();
|
||||
cache_map_.clear();
|
||||
stats_.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type size() const {
|
||||
if (!enabled_) return 0;
|
||||
std::shared_lock lock(mutex_);
|
||||
return cache_map_.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_type get_capacity() const { return capacity_; }
|
||||
|
||||
void resize(size_type new_capacity) {
|
||||
if (!enabled_) return;
|
||||
std::unique_lock lock(mutex_);
|
||||
capacity_ = new_capacity;
|
||||
shrink_if_needed();
|
||||
cache_map_.reserve(capacity_);
|
||||
}
|
||||
|
||||
void setEnabled(bool state) {
|
||||
std::unique_lock lock(mutex_);
|
||||
enabled_ = state;
|
||||
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled");
|
||||
if (!enabled_) clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isEnabled() const { return enabled_; }
|
||||
|
||||
[[nodiscard]] Statistics stats() const {
|
||||
std::shared_lock lock(mutex_);
|
||||
return stats_;
|
||||
}
|
||||
|
||||
private:
|
||||
using list_type = std::list<key_type>;
|
||||
using list_iterator = typename list_type::iterator;
|
||||
using map_value_type = std::pair<list_iterator, value_type>;
|
||||
using map_type = std::unordered_map<key_type, map_value_type>;
|
||||
|
||||
template <typename V>
|
||||
void insert_or_update(const key_type& key, V&& value) {
|
||||
auto it = cache_map_.find(key);
|
||||
if (it != cache_map_.end()) {
|
||||
it->second.second = std::forward<V>(value);
|
||||
move_to_front(it);
|
||||
return;
|
||||
}
|
||||
// evict LRU if full
|
||||
if (cache_map_.size() >= capacity_) {
|
||||
const auto& lru_key = cache_list_.back();
|
||||
cache_map_.erase(lru_key);
|
||||
cache_list_.pop_back();
|
||||
}
|
||||
cache_list_.push_front(key);
|
||||
cache_map_[key] = {cache_list_.begin(), std::forward<V>(value)};
|
||||
}
|
||||
|
||||
void move_to_front(typename map_type::iterator it) {
|
||||
cache_list_.splice(cache_list_.begin(), cache_list_, it->second.first);
|
||||
it->second.first = cache_list_.begin();
|
||||
}
|
||||
|
||||
void shrink_if_needed() {
|
||||
while (cache_map_.size() > capacity_) {
|
||||
const auto& lru_key = cache_list_.back();
|
||||
cache_map_.erase(lru_key);
|
||||
cache_list_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mutex_;
|
||||
bool enabled_{true};
|
||||
size_type capacity_;
|
||||
list_type cache_list_;
|
||||
map_type cache_map_;
|
||||
mutable Statistics stats_;
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <numeric>
|
||||
|
|
@ -17,23 +17,6 @@
|
|||
|
||||
namespace Core::NCE {
|
||||
|
||||
Patcher::Patcher(Patcher&& other) noexcept
|
||||
: patch_cache(std::move(other.patch_cache)),
|
||||
m_patch_instructions(std::move(other.m_patch_instructions)),
|
||||
c(m_patch_instructions),
|
||||
m_save_context(other.m_save_context),
|
||||
m_load_context(other.m_load_context),
|
||||
mode(other.mode),
|
||||
total_program_size(other.total_program_size),
|
||||
m_relocate_module_index(other.m_relocate_module_index),
|
||||
modules(std::move(other.modules)),
|
||||
curr_patch(nullptr) {
|
||||
if (!modules.empty()) {
|
||||
curr_patch = &modules.back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using namespace Common::Literals;
|
||||
using namespace oaknut::util;
|
||||
|
||||
|
|
@ -42,20 +25,23 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
|||
constexpr size_t MaxRelativeBranch = 128_MiB;
|
||||
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
|
||||
|
||||
Patcher::Patcher() : c(m_patch_instructions) {
|
||||
LOG_WARNING(Core_ARM, "Patcher initialized with LRU cache {}",
|
||||
patch_cache.isEnabled() ? "enabled" : "disabled");
|
||||
Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) {
|
||||
// The first word of the patch section is always a branch to the first instruction of the
|
||||
// module.
|
||||
c.dw(0);
|
||||
c_pre.dw(0);
|
||||
|
||||
// Write save context helper function.
|
||||
c.l(m_save_context);
|
||||
WriteSaveContext();
|
||||
c_pre.l(m_save_context_pre);
|
||||
WriteSaveContext(c_pre);
|
||||
|
||||
// Write load context helper function.
|
||||
c.l(m_load_context);
|
||||
WriteLoadContext();
|
||||
c_pre.l(m_load_context_pre);
|
||||
WriteLoadContext(c_pre);
|
||||
}
|
||||
|
||||
Patcher::~Patcher() = default;
|
||||
|
|
@ -64,7 +50,16 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
const Kernel::CodeSet::Segment& code) {
|
||||
// If we have patched modules but cannot reach the new module, then it needs its own patcher.
|
||||
const size_t image_size = program_image.size();
|
||||
if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
|
||||
|
||||
// Check if we need split mode for large modules. A64 max takes 128MB
|
||||
// tests showed that, with update, some are larger. (In this case 208MB)
|
||||
bool use_split = false;
|
||||
if (image_size > MaxRelativeBranch) {
|
||||
if (total_program_size > 0) {
|
||||
return false;
|
||||
}
|
||||
use_split = true;
|
||||
} else if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +69,12 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
|
||||
// The first word of the patch section is always a branch to the first instruction of the
|
||||
// module.
|
||||
curr_patch->m_branch_to_module_relocations.push_back({0, 0});
|
||||
if (use_split) {
|
||||
// curr_patch->m_branch_to_module_relocations.push_back({0, 0});
|
||||
curr_patch->m_branch_to_module_relocations_pre.push_back({0, 0});
|
||||
} else {
|
||||
curr_patch->m_branch_to_module_relocations.push_back({0, 0});
|
||||
}
|
||||
|
||||
// Retrieve text segment data.
|
||||
const auto text = std::span{program_image}.subspan(code.offset, code.size);
|
||||
|
|
@ -85,12 +85,18 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
for (u32 i = ModuleCodeIndex; i < static_cast<u32>(text_words.size()); i++) {
|
||||
const u32 inst = text_words[i];
|
||||
|
||||
const auto AddRelocations = [&] {
|
||||
const auto AddRelocations = [&](bool& pre_buffer) {
|
||||
const uintptr_t this_offset = i * sizeof(u32);
|
||||
const uintptr_t next_offset = this_offset + sizeof(u32);
|
||||
|
||||
// Relocate from here to patch.
|
||||
this->BranchToPatch(this_offset);
|
||||
pre_buffer = use_split && (this_offset < MaxRelativeBranch);
|
||||
|
||||
// Relocate to pre- or post-patch
|
||||
if (pre_buffer) {
|
||||
this->BranchToPatchPre(this_offset);
|
||||
} else {
|
||||
this->BranchToPatch(this_offset);
|
||||
}
|
||||
|
||||
// Relocate from patch to next instruction.
|
||||
return next_offset;
|
||||
|
|
@ -98,7 +104,13 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
|
||||
// SVC
|
||||
if (auto svc = SVC{inst}; svc.Verify()) {
|
||||
WriteSvcTrampoline(AddRelocations(), svc.GetValue());
|
||||
bool pre_buffer = false;
|
||||
auto ret = AddRelocations(pre_buffer);
|
||||
if (pre_buffer) {
|
||||
WriteSvcTrampoline(ret, svc.GetValue(), c_pre, m_save_context_pre, m_load_context_pre);
|
||||
} else {
|
||||
WriteSvcTrampoline(ret, svc.GetValue(), c, m_save_context, m_load_context);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -109,13 +121,25 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0
|
||||
: oaknut::SystemReg::TPIDR_EL0;
|
||||
const auto dest_reg = oaknut::XReg{static_cast<int>(mrs.GetRt())};
|
||||
WriteMrsHandler(AddRelocations(), dest_reg, src_reg);
|
||||
bool pre_buffer = false;
|
||||
auto ret = AddRelocations(pre_buffer);
|
||||
if (pre_buffer) {
|
||||
WriteMrsHandler(ret, dest_reg, src_reg, c_pre);
|
||||
} else {
|
||||
WriteMrsHandler(ret, dest_reg, src_reg, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// MRS Xn, CNTPCT_EL0
|
||||
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntpctEl0) {
|
||||
WriteCntpctHandler(AddRelocations(), oaknut::XReg{static_cast<int>(mrs.GetRt())});
|
||||
bool pre_buffer = false;
|
||||
auto ret = AddRelocations(pre_buffer);
|
||||
if (pre_buffer) {
|
||||
WriteCntpctHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c_pre);
|
||||
} else {
|
||||
WriteCntpctHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +150,13 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
|
||||
// MSR TPIDR_EL0, Xn
|
||||
if (auto msr = MSR{inst}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
|
||||
WriteMsrHandler(AddRelocations(), oaknut::XReg{static_cast<int>(msr.GetRt())});
|
||||
bool pre_buffer = false;
|
||||
auto ret = AddRelocations(pre_buffer);
|
||||
if (pre_buffer) {
|
||||
WriteMsrHandler(ret, oaknut::XReg{static_cast<int>(msr.GetRt())}, c_pre);
|
||||
} else {
|
||||
WriteMsrHandler(ret, oaknut::XReg{static_cast<int>(msr.GetRt())}, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +167,11 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||
|
||||
// Determine patching mode for the final relocation step
|
||||
total_program_size += image_size;
|
||||
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
||||
if (use_split) {
|
||||
this->mode = PatchMode::Split;
|
||||
} else {
|
||||
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +180,9 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||
Kernel::PhysicalMemory& program_image,
|
||||
EntryTrampolines* out_trampolines) {
|
||||
const size_t patch_size = GetSectionSize();
|
||||
const size_t image_size = program_image.size();
|
||||
const size_t pre_patch_size = GetPreSectionSize();
|
||||
|
||||
const size_t image_size = (mode == PatchMode::Split) ? program_image.size() - pre_patch_size : program_image.size();
|
||||
|
||||
// Retrieve text segment data.
|
||||
const auto text = std::span{program_image}.subspan(code.offset, code.size);
|
||||
|
|
@ -162,6 +198,16 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||
}
|
||||
};
|
||||
|
||||
const auto ApplyBranchToPatchRelocationPre = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
rc.B(static_cast<ptrdiff_t>(rel.patch_offset) - static_cast<ptrdiff_t>(pre_patch_size) - static_cast<ptrdiff_t>(rel.module_offset));
|
||||
};
|
||||
|
||||
const auto ApplyBranchToPatchRelocationPostSplit = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
rc.B(static_cast<ptrdiff_t>(image_size) + static_cast<ptrdiff_t>(rel.patch_offset) - static_cast<ptrdiff_t>(rel.module_offset));
|
||||
};
|
||||
|
||||
const auto ApplyBranchToModuleRelocation = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
if (mode == PatchMode::PreText) {
|
||||
|
|
@ -171,6 +217,16 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||
}
|
||||
};
|
||||
|
||||
const auto ApplyBranchToModuleRelocationPre = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
rc.B(static_cast<ptrdiff_t>(pre_patch_size) + static_cast<ptrdiff_t>(rel.module_offset) - static_cast<ptrdiff_t>(rel.patch_offset));
|
||||
};
|
||||
|
||||
const auto ApplyBranchToModuleRelocationPostSplit = [&](u32* target, const Relocation& rel) {
|
||||
oaknut::CodeGenerator rc{target};
|
||||
rc.B(static_cast<ptrdiff_t>(rel.module_offset) - static_cast<ptrdiff_t>(image_size) - static_cast<ptrdiff_t>(rel.patch_offset));
|
||||
};
|
||||
|
||||
const auto RebasePatch = [&](ptrdiff_t patch_offset) {
|
||||
if (mode == PatchMode::PreText) {
|
||||
return GetInteger(load_base) + patch_offset;
|
||||
|
|
@ -182,28 +238,87 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||
const auto RebasePc = [&](uintptr_t module_offset) {
|
||||
if (mode == PatchMode::PreText) {
|
||||
return GetInteger(load_base) + patch_size + module_offset;
|
||||
} else {
|
||||
return GetInteger(load_base) + module_offset;
|
||||
}
|
||||
if (mode == PatchMode::Split) {
|
||||
return GetInteger(load_base) + pre_patch_size + module_offset;
|
||||
}
|
||||
|
||||
return GetInteger(load_base) + module_offset;
|
||||
};
|
||||
|
||||
// We are now ready to relocate!
|
||||
auto& patch = modules[m_relocate_module_index++];
|
||||
for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
|
||||
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
for (const Relocation& rel : patch.m_branch_to_module_relocations) {
|
||||
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
|
||||
rel);
|
||||
|
||||
if (mode == PatchMode::Split) {
|
||||
for (const Relocation& rel : patch.m_branch_to_pre_patch_relocations) {
|
||||
ApplyBranchToPatchRelocationPre(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "applied Pre: {}", patch.m_branch_to_pre_patch_relocations.size());
|
||||
|
||||
for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
|
||||
ApplyBranchToPatchRelocationPostSplit(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "applied Post: {}", patch.m_branch_to_patch_relocations.size());
|
||||
|
||||
for (const Relocation& rel : patch.m_branch_to_module_relocations_pre) {
|
||||
ApplyBranchToModuleRelocationPre(m_patch_instructions_pre.data() + rel.patch_offset / sizeof(u32), rel);
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "aplied Pre-module {}", patch.m_branch_to_module_relocations_pre.size());
|
||||
|
||||
for (const Relocation& rel : patch.m_branch_to_module_relocations) {
|
||||
ApplyBranchToModuleRelocationPostSplit(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), rel);
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "applied Post-module {}", patch.m_branch_to_module_relocations.size());
|
||||
|
||||
// Pre
|
||||
for (const Relocation& rel : patch.m_write_module_pc_relocations_pre) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions_pre.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
// Post
|
||||
for (const Relocation& rel : patch.m_write_module_pc_relocations) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
|
||||
// Trampolines (split pre + post)
|
||||
for (const Trampoline& rel : patch.m_trampolines_pre) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset),
|
||||
GetInteger(load_base) + rel.patch_offset});
|
||||
}
|
||||
for (const Trampoline& rel : patch.m_trampolines) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset),
|
||||
GetInteger(load_base) + pre_patch_size + image_size + rel.patch_offset});
|
||||
}
|
||||
|
||||
if (!m_patch_instructions_pre.empty()) {
|
||||
u32 insn = m_patch_instructions_pre[0];
|
||||
if ((insn & 0xFC000000) == 0x14000000) {
|
||||
s32 imm26 = insn & 0x3FFFFFF;
|
||||
// Sign extend
|
||||
if (imm26 & 0x2000000) imm26 |= 0xFC000000;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
|
||||
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
|
||||
}
|
||||
for (const Relocation& rel : patch.m_branch_to_module_relocations) {
|
||||
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
|
||||
rel);
|
||||
}
|
||||
|
||||
// Rewrite PC constants
|
||||
for (const Relocation& rel : patch.m_write_module_pc_relocations) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite PC constants and record post trampolines
|
||||
for (const Relocation& rel : patch.m_write_module_pc_relocations) {
|
||||
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||
rc.dx(RebasePc(rel.module_offset));
|
||||
}
|
||||
for (const Trampoline& rel : patch.m_trampolines) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
|
||||
if (mode != PatchMode::Split) {
|
||||
for (const Trampoline& rel : patch.m_trampolines) {
|
||||
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
|
||||
}
|
||||
}
|
||||
|
||||
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
|
||||
|
|
@ -223,6 +338,15 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||
ASSERT(image_size == total_program_size);
|
||||
std::memcpy(program_image.data(), m_patch_instructions.data(),
|
||||
m_patch_instructions.size() * sizeof(u32));
|
||||
} else if (this->mode == PatchMode::Split) {
|
||||
const size_t current_size = program_image.size();
|
||||
program_image.resize(current_size + patch_size);
|
||||
// Copy pre-patch buffer to the beginning
|
||||
std::memcpy(program_image.data(), m_patch_instructions_pre.data(),
|
||||
m_patch_instructions_pre.size() * sizeof(u32));
|
||||
// Same for post-patch buffer to the end
|
||||
std::memcpy(program_image.data() + current_size, m_patch_instructions.data(),
|
||||
m_patch_instructions.size() * sizeof(u32));
|
||||
} else {
|
||||
program_image.resize(image_size + patch_size);
|
||||
std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
|
||||
|
|
@ -238,202 +362,225 @@ size_t Patcher::GetSectionSize() const noexcept {
|
|||
return Common::AlignUp(m_patch_instructions.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
|
||||
}
|
||||
|
||||
void Patcher::WriteLoadContext() {
|
||||
size_t Patcher::GetPreSectionSize() const noexcept {
|
||||
return Common::AlignUp(m_patch_instructions_pre.size() * sizeof(u32), Core::Memory::YUZU_PAGESIZE);
|
||||
}
|
||||
|
||||
void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) {
|
||||
// This function was called, which modifies X30, so use that as a scratch register.
|
||||
// SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes
|
||||
// of stack.
|
||||
c.STR(X30, SP, 8);
|
||||
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.STR(X30, SP, 8);
|
||||
cg.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
|
||||
// Load system registers.
|
||||
c.LDR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
c.MSR(oaknut::SystemReg::FPSR, X0);
|
||||
c.LDR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
c.MSR(oaknut::SystemReg::FPCR, X0);
|
||||
c.LDR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
c.MSR(oaknut::SystemReg::NZCV, X0);
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
cg.MSR(oaknut::SystemReg::FPSR, X0);
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
cg.MSR(oaknut::SystemReg::FPCR, X0);
|
||||
cg.LDR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
cg.MSR(oaknut::SystemReg::NZCV, X0);
|
||||
|
||||
// Load all vector registers.
|
||||
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
|
||||
for (int i = 0; i <= 30; i += 2) {
|
||||
c.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
cg.LDP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
}
|
||||
|
||||
// Load all general-purpose registers except X30.
|
||||
for (int i = 0; i <= 28; i += 2) {
|
||||
c.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
cg.LDP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
}
|
||||
|
||||
// Reload our return X30 from the stack and return.
|
||||
// The patch code will reload the guest X30 for us.
|
||||
c.LDR(X30, SP, 8);
|
||||
c.RET();
|
||||
cg.LDR(X30, SP, 8);
|
||||
cg.RET();
|
||||
}
|
||||
|
||||
void Patcher::WriteSaveContext() {
|
||||
void Patcher::WriteSaveContext(oaknut::VectorCodeGenerator& cg) {
|
||||
// This function was called, which modifies X30, so use that as a scratch register.
|
||||
// SP contains the guest X30, so save our X30 to SP + 8, since we have allocated 16 bytes of
|
||||
// stack.
|
||||
c.STR(X30, SP, 8);
|
||||
c.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.STR(X30, SP, 8);
|
||||
cg.MRS(X30, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X30, X30, offsetof(NativeExecutionParameters, native_context));
|
||||
|
||||
// Store all general-purpose registers except X30.
|
||||
for (int i = 0; i <= 28; i += 2) {
|
||||
c.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
cg.STP(oaknut::XReg{i}, oaknut::XReg{i + 1}, X30, 8 * i);
|
||||
}
|
||||
|
||||
// Store all vector registers.
|
||||
static constexpr size_t VEC_OFF = offsetof(GuestContext, vector_registers);
|
||||
for (int i = 0; i <= 30; i += 2) {
|
||||
c.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
cg.STP(oaknut::QReg{i}, oaknut::QReg{i + 1}, X30, VEC_OFF + 16 * i);
|
||||
}
|
||||
|
||||
// Store guest system registers, X30 and SP, using X0 as a scratch register.
|
||||
c.STR(X0, SP, PRE_INDEXED, -16);
|
||||
c.LDR(X0, SP, 16);
|
||||
c.STR(X0, X30, 8 * 30);
|
||||
c.ADD(X0, SP, 32);
|
||||
c.STR(X0, X30, offsetof(GuestContext, sp));
|
||||
c.MRS(X0, oaknut::SystemReg::FPSR);
|
||||
c.STR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
c.MRS(X0, oaknut::SystemReg::FPCR);
|
||||
c.STR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
c.MRS(X0, oaknut::SystemReg::NZCV);
|
||||
c.STR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
c.LDR(X0, SP, POST_INDEXED, 16);
|
||||
cg.STR(X0, SP, PRE_INDEXED, -16);
|
||||
cg.LDR(X0, SP, 16);
|
||||
cg.STR(X0, X30, 8 * 30);
|
||||
cg.ADD(X0, SP, 32);
|
||||
cg.STR(X0, X30, offsetof(GuestContext, sp));
|
||||
cg.MRS(X0, oaknut::SystemReg::FPSR);
|
||||
cg.STR(W0, X30, offsetof(GuestContext, fpsr));
|
||||
cg.MRS(X0, oaknut::SystemReg::FPCR);
|
||||
cg.STR(W0, X30, offsetof(GuestContext, fpcr));
|
||||
cg.MRS(X0, oaknut::SystemReg::NZCV);
|
||||
cg.STR(W0, X30, offsetof(GuestContext, nzcv));
|
||||
cg.LDR(X0, SP, POST_INDEXED, 16);
|
||||
|
||||
// Reload our return X30 from the stack, and return.
|
||||
c.LDR(X30, SP, 8);
|
||||
c.RET();
|
||||
cg.LDR(X30, SP, 8);
|
||||
cg.RET();
|
||||
}
|
||||
|
||||
void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
|
||||
void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& cg, oaknut::Label& save_ctx, oaknut::Label& load_ctx) {
|
||||
// Determine if we're writing to the pre-patch buffer
|
||||
const bool is_pre = (&cg == &c_pre);
|
||||
|
||||
// We are about to start saving state, so we need to lock the context.
|
||||
this->LockContext();
|
||||
this->LockContext(cg);
|
||||
|
||||
// Store guest X30 to the stack. Then, save the context and restore the stack.
|
||||
// This will save all registers except PC, but we know PC at patch time.
|
||||
c.STR(X30, SP, PRE_INDEXED, -16);
|
||||
c.BL(m_save_context);
|
||||
c.LDR(X30, SP, POST_INDEXED, 16);
|
||||
cg.STR(X30, SP, PRE_INDEXED, -16);
|
||||
cg.BL(save_ctx);
|
||||
cg.LDR(X30, SP, POST_INDEXED, 16);
|
||||
|
||||
// Now that we've saved all registers, we can use any registers as scratch.
|
||||
// Store PC + 4 to arm interface, since we know the instruction offset from the entry point.
|
||||
oaknut::Label pc_after_svc;
|
||||
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
c.LDR(X2, pc_after_svc);
|
||||
c.STR(X2, X1, offsetof(GuestContext, pc));
|
||||
cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.LDR(X2, pc_after_svc);
|
||||
cg.STR(X2, X1, offsetof(GuestContext, pc));
|
||||
|
||||
// Store SVC number to execute when we return
|
||||
c.MOV(X2, svc_id);
|
||||
c.STR(W2, X1, offsetof(GuestContext, svc));
|
||||
cg.MOV(X2, svc_id);
|
||||
cg.STR(W2, X1, offsetof(GuestContext, svc));
|
||||
|
||||
// We are calling a SVC. Clear esr_el1 and return it.
|
||||
static_assert(std::is_same_v<std::underlying_type_t<HaltReason>, u64>);
|
||||
oaknut::Label retry;
|
||||
c.ADD(X2, X1, offsetof(GuestContext, esr_el1));
|
||||
c.l(retry);
|
||||
c.LDAXR(X0, X2);
|
||||
c.STLXR(W3, XZR, X2);
|
||||
c.CBNZ(W3, retry);
|
||||
cg.ADD(X2, X1, offsetof(GuestContext, esr_el1));
|
||||
cg.l(retry);
|
||||
cg.LDAXR(X0, X2);
|
||||
cg.STLXR(W3, XZR, X2);
|
||||
cg.CBNZ(W3, retry);
|
||||
|
||||
// Add "calling SVC" flag. Since this is X0, this is now our return value.
|
||||
c.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
|
||||
cg.ORR(X0, X0, static_cast<u64>(HaltReason::SupervisorCall));
|
||||
|
||||
// Offset the GuestContext pointer to the HostContext member.
|
||||
// STP has limited range of [-512, 504] which we can't reach otherwise
|
||||
// NB: Due to this all offsets below are from the start of HostContext.
|
||||
c.ADD(X1, X1, offsetof(GuestContext, host_ctx));
|
||||
cg.ADD(X1, X1, offsetof(GuestContext, host_ctx));
|
||||
|
||||
// Reload host TPIDR_EL0 and SP.
|
||||
static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0));
|
||||
c.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
|
||||
c.MOV(SP, X2);
|
||||
c.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
|
||||
cg.LDP(X2, X3, X1, offsetof(HostContext, host_sp));
|
||||
cg.MOV(SP, X2);
|
||||
cg.MSR(oaknut::SystemReg::TPIDR_EL0, X3);
|
||||
|
||||
// Load callee-saved host registers and return to host.
|
||||
static constexpr size_t HOST_REGS_OFF = offsetof(HostContext, host_saved_regs);
|
||||
static constexpr size_t HOST_VREGS_OFF = offsetof(HostContext, host_saved_vregs);
|
||||
c.LDP(X19, X20, X1, HOST_REGS_OFF);
|
||||
c.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
|
||||
c.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
|
||||
c.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
|
||||
c.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
|
||||
c.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
|
||||
c.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
|
||||
c.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
|
||||
c.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
|
||||
c.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
|
||||
c.RET();
|
||||
cg.LDP(X19, X20, X1, HOST_REGS_OFF);
|
||||
cg.LDP(X21, X22, X1, HOST_REGS_OFF + 2 * sizeof(u64));
|
||||
cg.LDP(X23, X24, X1, HOST_REGS_OFF + 4 * sizeof(u64));
|
||||
cg.LDP(X25, X26, X1, HOST_REGS_OFF + 6 * sizeof(u64));
|
||||
cg.LDP(X27, X28, X1, HOST_REGS_OFF + 8 * sizeof(u64));
|
||||
cg.LDP(X29, X30, X1, HOST_REGS_OFF + 10 * sizeof(u64));
|
||||
cg.LDP(Q8, Q9, X1, HOST_VREGS_OFF);
|
||||
cg.LDP(Q10, Q11, X1, HOST_VREGS_OFF + 2 * sizeof(u128));
|
||||
cg.LDP(Q12, Q13, X1, HOST_VREGS_OFF + 4 * sizeof(u128));
|
||||
cg.LDP(Q14, Q15, X1, HOST_VREGS_OFF + 6 * sizeof(u128));
|
||||
cg.RET();
|
||||
|
||||
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its
|
||||
// state.
|
||||
curr_patch->m_trampolines.push_back({c.offset(), module_dest});
|
||||
if (is_pre) {
|
||||
curr_patch->m_trampolines_pre.push_back({cg.offset(), module_dest});
|
||||
} else {
|
||||
curr_patch->m_trampolines.push_back({cg.offset(), module_dest});
|
||||
}
|
||||
|
||||
// Host called this location. Save the return address so we can
|
||||
// unwind the stack properly when jumping back.
|
||||
c.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
|
||||
c.ADD(X0, X2, offsetof(GuestContext, host_ctx));
|
||||
c.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
|
||||
cg.MRS(X2, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X2, X2, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.ADD(X0, X2, offsetof(GuestContext, host_ctx));
|
||||
cg.STR(X30, X0, offsetof(HostContext, host_saved_regs) + 11 * sizeof(u64));
|
||||
|
||||
// Reload all guest registers except X30 and PC.
|
||||
// The function also expects 16 bytes of stack already allocated.
|
||||
c.STR(X30, SP, PRE_INDEXED, -16);
|
||||
c.BL(m_load_context);
|
||||
c.LDR(X30, SP, POST_INDEXED, 16);
|
||||
cg.STR(X30, SP, PRE_INDEXED, -16);
|
||||
cg.BL(load_ctx);
|
||||
cg.LDR(X30, SP, POST_INDEXED, 16);
|
||||
|
||||
// Use X1 as a scratch register to restore X30.
|
||||
c.STR(X1, SP, PRE_INDEXED, -16);
|
||||
c.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
c.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
|
||||
c.LDR(X1, SP, POST_INDEXED, 16);
|
||||
cg.STR(X1, SP, PRE_INDEXED, -16);
|
||||
cg.MRS(X1, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.LDR(X1, X1, offsetof(NativeExecutionParameters, native_context));
|
||||
cg.LDR(X30, X1, offsetof(GuestContext, cpu_registers) + sizeof(u64) * 30);
|
||||
cg.LDR(X1, SP, POST_INDEXED, 16);
|
||||
|
||||
// Unlock the context.
|
||||
this->UnlockContext();
|
||||
this->UnlockContext(cg);
|
||||
|
||||
// Jump back to the instruction after the emulated SVC.
|
||||
this->BranchToModule(module_dest);
|
||||
if (&cg == &c_pre)
|
||||
this->BranchToModulePre(module_dest);
|
||||
else
|
||||
this->BranchToModule(module_dest);
|
||||
|
||||
// Store PC after call.
|
||||
c.l(pc_after_svc);
|
||||
this->WriteModulePc(module_dest);
|
||||
cg.l(pc_after_svc);
|
||||
if (&cg == &c_pre)
|
||||
this->WriteModulePcPre(module_dest);
|
||||
else
|
||||
this->WriteModulePc(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
|
||||
oaknut::SystemReg src_reg) {
|
||||
oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
// Retrieve emulated TLS register from GuestContext.
|
||||
c.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.MRS(dest_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) {
|
||||
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
|
||||
cg.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0));
|
||||
} else {
|
||||
c.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
cg.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
}
|
||||
|
||||
// Jump back to the instruction after the emulated MRS.
|
||||
this->BranchToModule(module_dest);
|
||||
if (&cg == &c_pre)
|
||||
this->BranchToModulePre(module_dest);
|
||||
else
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) {
|
||||
void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
const auto scratch_reg = src_reg.index() == 0 ? X1 : X0;
|
||||
c.STR(scratch_reg, SP, PRE_INDEXED, -16);
|
||||
cg.STR(scratch_reg, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Save guest value to NativeExecutionParameters::tpidr_el0.
|
||||
c.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
cg.MRS(scratch_reg, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0));
|
||||
|
||||
// Restore scratch register.
|
||||
c.LDR(scratch_reg, SP, POST_INDEXED, 16);
|
||||
cg.LDR(scratch_reg, SP, POST_INDEXED, 16);
|
||||
|
||||
// Jump back to the instruction after the emulated MSR.
|
||||
this->BranchToModule(module_dest);
|
||||
if (&cg == &c_pre)
|
||||
this->BranchToModulePre(module_dest);
|
||||
else
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) {
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
static Common::Arm64::NativeClock clock{};
|
||||
const auto factor = clock.GetGuestCNTFRQFactor();
|
||||
const auto raw_factor = std::bit_cast<std::array<u64, 2>>(factor);
|
||||
|
|
@ -446,80 +593,83 @@ void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_
|
|||
oaknut::Label factorhi;
|
||||
|
||||
// Save scratches.
|
||||
c.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
|
||||
cg.STP(scratch0, scratch1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Load counter value.
|
||||
c.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
|
||||
cg.MRS(dest_reg, oaknut::SystemReg::CNTVCT_EL0);
|
||||
|
||||
// Load scaling factor.
|
||||
c.LDR(scratch0, factorlo);
|
||||
c.LDR(scratch1, factorhi);
|
||||
cg.LDR(scratch0, factorlo);
|
||||
cg.LDR(scratch1, factorhi);
|
||||
|
||||
// Multiply low bits and get result.
|
||||
c.UMULH(scratch0, dest_reg, scratch0);
|
||||
cg.UMULH(scratch0, dest_reg, scratch0);
|
||||
|
||||
// Multiply high bits and add low bit result.
|
||||
c.MADD(dest_reg, dest_reg, scratch1, scratch0);
|
||||
cg.MADD(dest_reg, dest_reg, scratch1, scratch0);
|
||||
|
||||
// Reload scratches.
|
||||
c.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
|
||||
cg.LDP(scratch0, scratch1, SP, POST_INDEXED, 16);
|
||||
|
||||
// Jump back to the instruction after the emulated MRS.
|
||||
this->BranchToModule(module_dest);
|
||||
if (&cg == &c_pre)
|
||||
this->BranchToModulePre(module_dest);
|
||||
else
|
||||
this->BranchToModule(module_dest);
|
||||
|
||||
// Scaling factor constant values.
|
||||
c.l(factorlo);
|
||||
c.dx(raw_factor[0]);
|
||||
c.l(factorhi);
|
||||
c.dx(raw_factor[1]);
|
||||
cg.l(factorlo);
|
||||
cg.dx(raw_factor[0]);
|
||||
cg.l(factorhi);
|
||||
cg.dx(raw_factor[1]);
|
||||
}
|
||||
|
||||
void Patcher::LockContext() {
|
||||
void Patcher::LockContext(oaknut::VectorCodeGenerator& cg) {
|
||||
oaknut::Label retry;
|
||||
|
||||
// Save scratches.
|
||||
c.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
cg.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Reload lock pointer.
|
||||
c.l(retry);
|
||||
c.CLREX();
|
||||
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
cg.l(retry);
|
||||
cg.CLREX();
|
||||
cg.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
|
||||
static_assert(SpinLockLocked == 0);
|
||||
|
||||
// Load-linked with acquire ordering.
|
||||
c.LDAXR(W1, X0);
|
||||
cg.LDAXR(W1, X0);
|
||||
|
||||
// If the value was SpinLockLocked, clear monitor and retry.
|
||||
c.CBZ(W1, retry);
|
||||
cg.CBZ(W1, retry);
|
||||
|
||||
// Store-conditional SpinLockLocked with relaxed ordering.
|
||||
c.STXR(W1, WZR, X0);
|
||||
cg.STXR(W1, WZR, X0);
|
||||
|
||||
// If we failed to store, retry.
|
||||
c.CBNZ(W1, retry);
|
||||
cg.CBNZ(W1, retry);
|
||||
|
||||
// We succeeded! Reload scratches.
|
||||
c.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
cg.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
}
|
||||
|
||||
void Patcher::UnlockContext() {
|
||||
void Patcher::UnlockContext(oaknut::VectorCodeGenerator& cg) {
|
||||
// Save scratches.
|
||||
c.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
cg.STP(X0, X1, SP, PRE_INDEXED, -16);
|
||||
|
||||
// Load lock pointer.
|
||||
c.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
c.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
cg.MRS(X0, oaknut::SystemReg::TPIDR_EL0);
|
||||
cg.ADD(X0, X0, offsetof(NativeExecutionParameters, lock));
|
||||
|
||||
// Load SpinLockUnlocked.
|
||||
c.MOV(W1, SpinLockUnlocked);
|
||||
cg.MOV(W1, SpinLockUnlocked);
|
||||
|
||||
// Store value with release ordering.
|
||||
c.STLR(W1, X0);
|
||||
cg.STLR(W1, X0);
|
||||
|
||||
// Load scratches.
|
||||
c.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
cg.LDP(X0, X1, SP, POST_INDEXED, 16);
|
||||
}
|
||||
|
||||
} // namespace Core::NCE
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
#include <oaknut/code_block.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/k_typed_address.h"
|
||||
#include "core/hle/kernel/physical_memory.h"
|
||||
#include "lru_cache.h"
|
||||
#include <utility>
|
||||
using ModuleID = std::array<u8, 32>; // NSO build ID
|
||||
struct PatchCacheKey {
|
||||
|
|
@ -41,21 +41,19 @@ enum class PatchMode : u32 {
|
|||
None,
|
||||
PreText, ///< Patch section is inserted before .text
|
||||
PostData, ///< Patch section is inserted after .data
|
||||
Split, ///< Patch sections are inserted before .text and after .data
|
||||
};
|
||||
|
||||
|
||||
using ModuleTextAddress = u64;
|
||||
using PatchTextAddress = u64;
|
||||
using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
||||
using EntryTrampolines = ankerl::unordered_dense::map<ModuleTextAddress, PatchTextAddress>;
|
||||
|
||||
class Patcher {
|
||||
public:
|
||||
void SetModuleID(const ModuleID& id) {
|
||||
module_id = id;
|
||||
}
|
||||
Patcher(const Patcher&) = delete;
|
||||
Patcher& operator=(const Patcher&) = delete;
|
||||
Patcher(Patcher&& other) noexcept;
|
||||
Patcher& operator=(Patcher&&) noexcept = delete;
|
||||
explicit Patcher();
|
||||
~Patcher();
|
||||
bool PatchText(const Kernel::PhysicalMemory& program_image,
|
||||
|
|
@ -63,6 +61,7 @@ public:
|
|||
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
|
||||
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
|
||||
size_t GetSectionSize() const noexcept;
|
||||
size_t GetPreSectionSize() const noexcept;
|
||||
|
||||
[[nodiscard]] PatchMode GetPatchMode() const noexcept {
|
||||
return mode;
|
||||
|
|
@ -76,39 +75,34 @@ private:
|
|||
uintptr_t module_offset;
|
||||
};
|
||||
|
||||
void WriteLoadContext();
|
||||
void WriteSaveContext();
|
||||
void LockContext();
|
||||
void UnlockContext();
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id);
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
|
||||
oaknut::SystemReg src_reg);
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg);
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
|
||||
// Core implementations with explicit code generator
|
||||
void WriteLoadContext(oaknut::VectorCodeGenerator& code);
|
||||
void WriteSaveContext(oaknut::VectorCodeGenerator& code);
|
||||
void LockContext(oaknut::VectorCodeGenerator& code);
|
||||
void UnlockContext(oaknut::VectorCodeGenerator& code);
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& code, oaknut::Label& save_ctx, oaknut::Label& load_ctx);
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code);
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code);
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
|
||||
|
||||
// Convenience wrappers using default code generator
|
||||
void WriteLoadContext() { WriteLoadContext(c); }
|
||||
void WriteSaveContext() { WriteSaveContext(c); }
|
||||
void LockContext() { LockContext(c); }
|
||||
void UnlockContext() { UnlockContext(c); }
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { WriteSvcTrampoline(module_dest, svc_id, c, m_save_context, m_load_context); }
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg) { WriteMrsHandler(module_dest, dest_reg, src_reg, c); }
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { WriteMsrHandler(module_dest, src_reg, c); }
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntpctHandler(module_dest, dest_reg, c); }
|
||||
|
||||
private:
|
||||
static constexpr size_t CACHE_SIZE = 16384; // Cache size for patch entries
|
||||
LRUCache<PatchCacheKey, PatchTextAddress> patch_cache{CACHE_SIZE, Settings::values.lru_cache_enabled.GetValue()};
|
||||
|
||||
void BranchToPatch(uintptr_t module_dest) {
|
||||
if (patch_cache.isEnabled()) {
|
||||
PatchCacheKey key{module_id, module_dest};
|
||||
LOG_DEBUG(Core_ARM, "LRU cache lookup for module={}, offset={:#x}", fmt::ptr(module_id.data()), module_dest);
|
||||
// Try to get existing patch entry from cache
|
||||
if (auto* cached_patch = patch_cache.get(key)) {
|
||||
LOG_WARNING(Core_ARM, "LRU cache hit for module offset {:#x}", module_dest);
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), *cached_patch});
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "LRU cache miss for module offset {:#x}, creating new patch", module_dest);
|
||||
// Not in cache: create and store
|
||||
const auto patch_addr = c.offset();
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({patch_addr, module_dest});
|
||||
patch_cache.put(key, patch_addr);
|
||||
} else {
|
||||
LOG_DEBUG(Core_ARM, "LRU cache disabled - direct patch for offset {:#x}", module_dest);
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||
}
|
||||
LOG_DEBUG(Core_ARM, "Patch for offset {:#x}", module_dest);
|
||||
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||
}
|
||||
|
||||
void BranchToPatchPre(uintptr_t module_dest) {
|
||||
curr_patch->m_branch_to_pre_patch_relocations.push_back({c_pre.offset(), module_dest});
|
||||
}
|
||||
|
||||
void BranchToModule(uintptr_t module_dest) {
|
||||
|
|
@ -116,14 +110,25 @@ private:
|
|||
c.dw(0);
|
||||
}
|
||||
|
||||
void BranchToModulePre(uintptr_t module_dest) {
|
||||
curr_patch->m_branch_to_module_relocations_pre.push_back({c_pre.offset(), module_dest});
|
||||
c_pre.dw(0);
|
||||
}
|
||||
|
||||
void WriteModulePc(uintptr_t module_dest) {
|
||||
curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
||||
c.dx(0);
|
||||
}
|
||||
|
||||
void WriteModulePcPre(uintptr_t module_dest) {
|
||||
curr_patch->m_write_module_pc_relocations_pre.push_back({c_pre.offset(), module_dest});
|
||||
c_pre.dx(0);
|
||||
}
|
||||
|
||||
private:
|
||||
// List of patch instructions we have generated.
|
||||
std::vector<u32> m_patch_instructions{};
|
||||
std::vector<u32> m_patch_instructions_pre{};
|
||||
|
||||
// Relocation type for relative branch from module to patch.
|
||||
struct Relocation {
|
||||
|
|
@ -133,15 +138,22 @@ private:
|
|||
|
||||
struct ModulePatch {
|
||||
std::vector<Trampoline> m_trampolines;
|
||||
std::vector<Trampoline> m_trampolines_pre;
|
||||
std::vector<Relocation> m_branch_to_patch_relocations{};
|
||||
std::vector<Relocation> m_branch_to_pre_patch_relocations{};
|
||||
std::vector<Relocation> m_branch_to_module_relocations{};
|
||||
std::vector<Relocation> m_branch_to_module_relocations_pre{};
|
||||
std::vector<Relocation> m_write_module_pc_relocations{};
|
||||
std::vector<Relocation> m_write_module_pc_relocations_pre{};
|
||||
std::vector<ModuleTextAddress> m_exclusives{};
|
||||
};
|
||||
|
||||
oaknut::VectorCodeGenerator c;
|
||||
oaknut::VectorCodeGenerator c_pre;
|
||||
oaknut::Label m_save_context{};
|
||||
oaknut::Label m_load_context{};
|
||||
oaknut::Label m_save_context_pre{};
|
||||
oaknut::Label m_load_context_pre{};
|
||||
PatchMode mode{PatchMode::None};
|
||||
size_t total_program_size{};
|
||||
size_t m_relocate_module_index{};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
|
|
@ -13,10 +13,6 @@
|
|||
#include "common/windows/timer_resolution.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_wait.h"
|
||||
#endif
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
|
@ -287,28 +283,7 @@ void CoreTiming::ThreadLoop() {
|
|||
if (next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (event.IsSet()) {
|
||||
event.Reset();
|
||||
}
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
#include "core/file_sys/vfs/vfs_layered.h"
|
||||
|
||||
|
|
@ -60,7 +63,7 @@ std::string LayeredVfsDirectory::GetFullPath() const {
|
|||
|
||||
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
std::vector<VirtualFile> out;
|
||||
std::unordered_set<std::string> out_names;
|
||||
ankerl::unordered_dense::set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (auto& file : layer->GetFiles()) {
|
||||
|
|
@ -76,7 +79,7 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
|||
|
||||
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> out;
|
||||
std::unordered_set<std::string> out_names;
|
||||
ankerl::unordered_dense::set<std::string> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (const auto& sd : layer->GetSubdirectories()) {
|
||||
|
|
|
|||
|
|
@ -1,3 +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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -83,6 +86,14 @@ struct CodeSet final {
|
|||
const Segment& PatchSegment() const {
|
||||
return patch_segment;
|
||||
}
|
||||
|
||||
Segment& PostPatchSegment() {
|
||||
return post_patch_segment;
|
||||
}
|
||||
|
||||
const Segment& PostPatchSegment() const {
|
||||
return post_patch_segment;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The overall data that backs this code set.
|
||||
|
|
@ -93,6 +104,7 @@ struct CodeSet final {
|
|||
|
||||
#ifdef HAS_NCE
|
||||
Segment patch_segment;
|
||||
Segment post_patch_segment;
|
||||
#endif
|
||||
|
||||
/// The entry point address for this code set.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -1258,6 +1258,7 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
|||
|
||||
#ifdef HAS_NCE
|
||||
const auto& patch = code_set.PatchSegment();
|
||||
const auto& post_patch = code_set.PostPatchSegment();
|
||||
if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
|
||||
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
||||
const auto& code = code_set.CodeSegment();
|
||||
|
|
@ -1265,7 +1266,15 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
|||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
|
||||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
// Protect post-patch segment if it exists like abve
|
||||
if (post_patch.size != 0) {
|
||||
buffer.Protect(GetInteger(base_addr + post_patch.addr), post_patch.size,
|
||||
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
|
||||
}
|
||||
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
|
||||
if (post_patch.size != 0) {
|
||||
ReprotectSegment(code_set.PostPatchSegment(), Svc::MemoryPermission::None);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
|
|
@ -78,7 +78,7 @@ private:
|
|||
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
||||
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
|
||||
#ifdef HAS_NCE
|
||||
std::unordered_map<u64, u64> m_post_handlers{};
|
||||
ankerl::unordered_dense::map<u64, u64> m_post_handlers{};
|
||||
#endif
|
||||
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
|
||||
Core::Memory::Memory m_memory;
|
||||
|
|
@ -493,7 +493,7 @@ public:
|
|||
static void Switch(KProcess* cur_process, KProcess* next_process);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
std::unordered_map<u64, u64>& GetPostHandlers() noexcept {
|
||||
ankerl::unordered_dense::map<u64, u64>& GetPostHandlers() noexcept {
|
||||
return m_post_handlers;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
|
@ -786,8 +786,8 @@ struct KernelCore::Impl {
|
|||
|
||||
std::optional<KObjectNameGlobalData> object_name_global_data;
|
||||
|
||||
std::unordered_set<KAutoObject*> registered_objects;
|
||||
std::unordered_set<KAutoObject*> registered_in_use_objects;
|
||||
ankerl::unordered_dense::set<KAutoObject*> registered_objects;
|
||||
ankerl::unordered_dense::set<KAutoObject*> registered_in_use_objects;
|
||||
|
||||
std::mutex server_lock;
|
||||
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
|
|
@ -176,6 +176,6 @@ struct WebCommonReturnValue {
|
|||
};
|
||||
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
|
||||
|
||||
using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>;
|
||||
using WebArgInputTLVMap = ankerl::unordered_dense::map<WebArgInputTLVType, std::vector<u8>>;
|
||||
|
||||
} // namespace Service::AM::Frontend
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ std::vector<u8> default_logo_small;
|
|||
std::vector<u8> default_logo_large;
|
||||
bool default_logos_loaded = false;
|
||||
|
||||
std::unordered_map<std::string, std::vector<u8>> news_images_small;
|
||||
std::unordered_map<std::string, std::vector<u8>> news_images_large;
|
||||
ankerl::unordered_dense::map<std::string, std::vector<u8>> news_images_small;
|
||||
ankerl::unordered_dense::map<std::string, std::vector<u8>> news_images_large;
|
||||
std::mutex images_mutex;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -93,7 +93,7 @@ private:
|
|||
static s64 Now();
|
||||
|
||||
mutable std::mutex mtx;
|
||||
std::unordered_map<std::string, StoredNews> items;
|
||||
ankerl::unordered_dense::map<std::string, StoredNews> items;
|
||||
size_t open_counter{};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/fs/fs.h"
|
||||
#include "core/hle/result.h"
|
||||
|
|
@ -85,7 +88,7 @@ private:
|
|||
AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const;
|
||||
|
||||
bool is_mounted{};
|
||||
std::unordered_map<AlbumFileId, std::filesystem::path> album_files;
|
||||
ankerl::unordered_dense::map<AlbumFileId, std::filesystem::path> album_files;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
#include <random>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/socket_types.h"
|
||||
|
|
@ -119,7 +119,7 @@ protected:
|
|||
std::array<LanStation, StationCountMax> stations;
|
||||
std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
|
||||
std::array<u8, NodeCountMax> node_last_states{};
|
||||
std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
||||
ankerl::unordered_dense::map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
|
||||
NodeInfo node_info{};
|
||||
NetworkInfo network_info{};
|
||||
State state{State::None};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
|
|
@ -331,7 +331,7 @@ private:
|
|||
};
|
||||
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
|
||||
|
||||
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
ankerl::unordered_dense::map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
LogDestination destination{LogDestination::All};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <common/settings.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "core/device_memory_manager.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
@ -9,7 +12,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
|
|
@ -158,7 +161,7 @@ private:
|
|||
std::list<std::shared_ptr<Handle>> unmap_queue{};
|
||||
std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
|
||||
|
||||
std::unordered_map<Handle::Id, std::shared_ptr<Handle>>
|
||||
ankerl::unordered_dense::map<Handle::Id, std::shared_ptr<Handle>>
|
||||
handles{}; //!< Main owning map of handles
|
||||
std::mutex handles_lock; //!< Protects access to `handles`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/address_space.h"
|
||||
|
|
@ -113,7 +113,7 @@ private:
|
|||
};
|
||||
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
|
||||
|
||||
std::unordered_set<s64_le> map_buffer_offsets{};
|
||||
ankerl::unordered_dense::set<s64_le> map_buffer_offsets{};
|
||||
|
||||
struct IoctlMapBufferEx {
|
||||
MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -210,7 +210,7 @@ private:
|
|||
NvCore::SyncpointManager& syncpoint_manager;
|
||||
NvCore::NvMap& nvmap;
|
||||
std::shared_ptr<Tegra::Control::ChannelState> channel_state;
|
||||
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
|
||||
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> sessions;
|
||||
u32 channel_syncpoint;
|
||||
std::mutex channel_mutex;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -128,7 +131,7 @@ protected:
|
|||
NvCore::NvMap& nvmap;
|
||||
NvCore::ChannelType channel_type;
|
||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
|
||||
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> sessions;
|
||||
};
|
||||
}; // namespace Devices
|
||||
} // namespace Service::Nvidia
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -115,7 +118,7 @@ private:
|
|||
|
||||
NvCore::Container& container;
|
||||
NvCore::NvMap& file;
|
||||
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
|
||||
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> sessions;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
@ -9,7 +12,7 @@
|
|||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
|
@ -100,7 +103,7 @@ private:
|
|||
/// Id to use for the next open file descriptor.
|
||||
DeviceFD next_fd = 1;
|
||||
|
||||
using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
|
||||
using FilesContainerType = ankerl::unordered_dense::map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
|
||||
/// Mapping of file descriptors to the devices they reference.
|
||||
FilesContainerType open_files;
|
||||
|
||||
|
|
@ -108,7 +111,7 @@ private:
|
|||
|
||||
EventInterface events_interface;
|
||||
|
||||
std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
|
||||
ankerl::unordered_dense::map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
|
@ -5,7 +8,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvnflinger/binder.h"
|
||||
|
|
@ -36,8 +39,8 @@ private:
|
|||
|
||||
mutable std::mutex lock;
|
||||
s32 last_id = 0;
|
||||
std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
|
||||
std::unordered_map<s32, RefCounts> refcounts;
|
||||
ankerl::unordered_dense::map<s32, std::shared_ptr<android::IBinder>> binders;
|
||||
ankerl::unordered_dense::map<s32, RefCounts> refcounts;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
|
|
@ -54,10 +54,10 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
std::unordered_map<AppKey, bool, AppKeyHash> app_auto_transfer_{};
|
||||
std::unordered_map<Common::UUID, bool> global_auto_upload_{};
|
||||
std::unordered_map<Common::UUID, bool> global_auto_download_{};
|
||||
std::unordered_map<AppKey, u8, AppKeyHash> autonomy_task_status_{};
|
||||
ankerl::unordered_dense::map<AppKey, bool, AppKeyHash> app_auto_transfer_{};
|
||||
ankerl::unordered_dense::map<Common::UUID, bool> global_auto_upload_{};
|
||||
ankerl::unordered_dense::map<Common::UUID, bool> global_auto_download_{};
|
||||
ankerl::unordered_dense::map<AppKey, u8, AppKeyHash> autonomy_task_status_{};
|
||||
};
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <concepts>
|
||||
|
||||
#include "core/hle/kernel/k_port.h"
|
||||
|
|
@ -97,8 +97,8 @@ private:
|
|||
|
||||
/// Map of registered services, retrieved using GetServicePort.
|
||||
std::mutex lock;
|
||||
std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
|
||||
std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
|
||||
ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services;
|
||||
ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports;
|
||||
|
||||
/// Kernel context
|
||||
Kernel::KernelCore& kernel;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
|
|
@ -45,7 +48,7 @@ private:
|
|||
private:
|
||||
Core::System& m_system;
|
||||
Container& m_container;
|
||||
std::unordered_map<u64, VsyncManager> m_vsync_managers;
|
||||
ankerl::unordered_dense::map<u64, VsyncManager> m_vsync_managers;
|
||||
std::shared_ptr<Core::Timing::EventType> m_event;
|
||||
Common::Event m_signal;
|
||||
std::jthread m_thread;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
namespace Core::LaunchTimestampCache {
|
||||
namespace {
|
||||
|
||||
using CacheMap = std::unordered_map<u64, s64>;
|
||||
using CountMap = std::unordered_map<u64, u64>;
|
||||
using CacheMap = ankerl::unordered_dense::map<u64, s64>;
|
||||
using CountMap = ankerl::unordered_dense::map<u64, u64>;
|
||||
|
||||
std::mutex g_mutex;
|
||||
CacheMap g_cache;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
|
|
@ -102,6 +102,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||
auto* patch = &patches->operator[](patch_index);
|
||||
if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
|
||||
return patch->GetSectionSize();
|
||||
} else if (patch->GetPatchMode() == Core::NCE::PatchMode::Split) {
|
||||
return patch->GetPreSectionSize();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -178,12 +180,26 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||
}
|
||||
} else if (patch) {
|
||||
// Relocate code patch and copy to the program_image.
|
||||
// Save size before RelocateAndCopy (which may resize)
|
||||
const size_t size_before_relocate = program_image.size();
|
||||
if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) {
|
||||
// Update patch section.
|
||||
auto& patch_segment = codeset.PatchSegment();
|
||||
patch_segment.addr =
|
||||
patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
|
||||
patch_segment.size = static_cast<u32>(patch->GetSectionSize());
|
||||
auto& post_patch_segment = codeset.PostPatchSegment();
|
||||
const auto patch_mode = patch->GetPatchMode();
|
||||
if (patch_mode == Core::NCE::PatchMode::PreText) {
|
||||
patch_segment.addr = 0;
|
||||
patch_segment.size = static_cast<u32>(patch->GetSectionSize());
|
||||
} else if (patch_mode == Core::NCE::PatchMode::Split) {
|
||||
// For Split-mode, we are using pre-patch buffer at start, post-patch buffer at end
|
||||
patch_segment.addr = 0;
|
||||
patch_segment.size = static_cast<u32>(patch->GetPreSectionSize());
|
||||
post_patch_segment.addr = size_before_relocate;
|
||||
post_patch_segment.size = static_cast<u32>(patch->GetSectionSize());
|
||||
} else {
|
||||
patch_segment.addr = image_size;
|
||||
patch_segment.size = static_cast<u32>(patch->GetSectionSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh image_size to take account the patch section if it was added by RelocateAndCopy
|
||||
|
|
@ -193,6 +209,18 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||
|
||||
// If we aren't actually loading (i.e. just computing the process code layout), we are done
|
||||
if (!load_into_process) {
|
||||
#ifdef HAS_NCE
|
||||
// Ok, so for Split mode, we need to account for pre-patch and post-patch space
|
||||
// which will be added during RelocateAndCopy in the second pass. Where it crashed
|
||||
// in Android Studio at PreText. May be a better way. Works for now.
|
||||
if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::Split) {
|
||||
return load_base + patch->GetPreSectionSize() + image_size + patch->GetSectionSize();
|
||||
} else if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
|
||||
return load_base + patch->GetSectionSize() + image_size;
|
||||
} else if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
|
||||
return load_base + image_size + patch->GetSectionSize();
|
||||
}
|
||||
#endif
|
||||
return load_base + image_size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ endif()
|
|||
|
||||
find_package(fmt 8 CONFIG)
|
||||
find_package(mcl 0.1.12 REQUIRED)
|
||||
find_package(unordered_dense REQUIRED)
|
||||
|
||||
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
|
||||
find_package(oaknut 2.0.1 CONFIG)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
include(TargetArchitectureSpecificSources)
|
||||
|
||||
|
|
@ -356,20 +356,10 @@ set_target_properties(dynarmic PROPERTIES
|
|||
SOVERSION ${dynarmic_VERSION_MAJOR}.${dynarmic_VERSION_MINOR}
|
||||
)
|
||||
|
||||
if (TARGET unordered_dense::unordered_dense)
|
||||
# weird quirk of system installs
|
||||
target_link_libraries(dynarmic
|
||||
PRIVATE
|
||||
unordered_dense::unordered_dense
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(dynarmic PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||
target_link_libraries(dynarmic
|
||||
PUBLIC
|
||||
fmt::fmt
|
||||
merry::mcl
|
||||
)
|
||||
|
||||
target_link_libraries(dynarmic PRIVATE unordered_dense::unordered_dense)
|
||||
target_link_libraries(dynarmic PUBLIC fmt::fmt merry::mcl)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "dynarmic/common/common_types.h"
|
||||
|
|
@ -23,7 +23,7 @@ namespace {
|
|||
class MyEnvironment final : public A64::UserCallbacks {
|
||||
public:
|
||||
u64 ticks_left = 0;
|
||||
std::unordered_map<u64, u8> memory{};
|
||||
ankerl::unordered_dense::map<u64, u8> memory{};
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return memory[vaddr];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/common/assert.h"
|
||||
#include "dynarmic/common/common_types.h"
|
||||
#include "dynarmic/interface/A64/a64.h"
|
||||
|
|
@ -23,7 +23,7 @@ public:
|
|||
u64 code_mem_start_address = 0;
|
||||
std::vector<u32> code_mem;
|
||||
|
||||
std::unordered_map<u64, u8> modified_memory;
|
||||
ankerl::unordered_dense::map<u64, u8> modified_memory;
|
||||
std::vector<std::string> interrupts;
|
||||
|
||||
bool IsInCodeMem(u64 vaddr) const {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ std::vector<std::filesystem::path> GetModFolder(const std::string& root) {
|
|||
|
||||
auto callback = [&paths](const std::filesystem::directory_entry& entry) -> bool {
|
||||
const auto name = entry.path().filename().string();
|
||||
static constexpr const std::array<std::string, 5> valid_names = {"exefs",
|
||||
"romfs"
|
||||
"romfs_ext",
|
||||
"cheats", "romfslite"};
|
||||
static const std::array<std::string, 5> valid_names = {"exefs",
|
||||
"romfs"
|
||||
"romfs_ext",
|
||||
"cheats", "romfslite"};
|
||||
|
||||
if (std::ranges::find(valid_names, name) != valid_names.end()) {
|
||||
paths.emplace_back(entry.path().parent_path());
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,7 +11,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -182,7 +185,7 @@ private:
|
|||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
|
||||
ankerl::unordered_dense::map<int, ConsoleUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
// Stores the current status of all console input
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -636,7 +636,7 @@ private:
|
|||
mutable std::shared_mutex callback_mutex;
|
||||
mutable std::shared_mutex npad_mutex;
|
||||
mutable std::shared_mutex connect_mutex;
|
||||
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
||||
ankerl::unordered_dense::map<int, ControllerUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
// Stores the current status of all controller input
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -7,7 +10,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -202,7 +205,7 @@ private:
|
|||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
|
||||
ankerl::unordered_dense::map<int, InterfaceUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
// Stores the current status of all external device input
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
|
@ -87,7 +90,7 @@ public:
|
|||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
private:
|
||||
std::unordered_map<PadIdentifier, jobject> input_devices;
|
||||
ankerl::unordered_dense::map<PadIdentifier, jobject> input_devices;
|
||||
|
||||
/// Returns the correct identifier corresponding to the player index
|
||||
PadIdentifier GetIdentifier(const std::string& guid, size_t port) const;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
@ -570,7 +570,7 @@ SDLDriver::~SDLDriver() {
|
|||
|
||||
std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
|
||||
ankerl::unordered_dense::map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
|
||||
for (const auto& [key, value] : joystick_map) {
|
||||
for (const auto& joystick : value) {
|
||||
if (!joystick->GetSDLJoystick()) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
|
|
@ -116,7 +119,7 @@ private:
|
|||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
ankerl::unordered_dense::map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
bool start_thread = false;
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -5,7 +8,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
|
|
@ -267,10 +270,10 @@ protected:
|
|||
|
||||
private:
|
||||
struct ControllerData {
|
||||
std::unordered_map<int, bool> buttons;
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
ankerl::unordered_dense::map<int, bool> buttons;
|
||||
ankerl::unordered_dense::map<int, u8> hat_buttons;
|
||||
ankerl::unordered_dense::map<int, float> axes;
|
||||
ankerl::unordered_dense::map<int, BasicMotion> motions;
|
||||
Common::Input::BatteryLevel battery{};
|
||||
Common::Input::BodyColorStatus color{};
|
||||
Common::Input::CameraStatus camera{};
|
||||
|
|
@ -298,8 +301,8 @@ private:
|
|||
bool configuring{false};
|
||||
const std::string input_engine;
|
||||
int last_callback_key = 0;
|
||||
std::unordered_map<PadIdentifier, ControllerData> controller_list;
|
||||
std::unordered_map<int, InputIdentifier> callback_list;
|
||||
ankerl::unordered_dense::map<PadIdentifier, ControllerData> controller_list;
|
||||
ankerl::unordered_dense::map<int, InputIdentifier> callback_list;
|
||||
MappingCallback mapping_callback;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -5,7 +8,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
|
@ -53,9 +56,9 @@ enum class InputType { None, Button, Stick, Motion, Touch };
|
|||
* Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
|
||||
* mapping for the device.
|
||||
*/
|
||||
using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
|
||||
using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
|
||||
using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
|
||||
using AnalogMapping = ankerl::unordered_dense::map<Settings::NativeAnalog::Values, Common::ParamPackage>;
|
||||
using ButtonMapping = ankerl::unordered_dense::map<Settings::NativeButton::Values, Common::ParamPackage>;
|
||||
using MotionMapping = ankerl::unordered_dense::map<Settings::NativeMotion::Values, Common::ParamPackage>;
|
||||
|
||||
class InputSubsystem {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@ struct Values {
|
|||
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
|
||||
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
|
||||
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList};
|
||||
Setting<bool> show_game_name{linkage, true, "show_game_name", Category::UiGameList};
|
||||
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ QStringList GetModFolders(const QString& root, const QString& fallbackName) {
|
|||
QString name = QtCommon::Frontend::GetTextInput(
|
||||
tr("Mod Name"), tr("What should this mod be called?"), default_name);
|
||||
|
||||
if (name.isEmpty()) return {};
|
||||
|
||||
// if std_path is empty, frontend_common could not determine mod type and/or name.
|
||||
// so we have to prompt the user and set up the structure ourselves
|
||||
if (paths.empty()) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/program_header.h"
|
||||
|
|
@ -125,9 +125,9 @@ protected:
|
|||
u32 start_address{};
|
||||
bool is_proprietary_driver{};
|
||||
public:
|
||||
std::unordered_map<CbufWordKey, u32, CbufWordKeyHash> cbuf_word_cache;
|
||||
std::unordered_map<HandleKey, u32, HandleKeyHash> handle_cache;
|
||||
std::unordered_map<const IR::Inst*, ConstBufferAddr> track_cache;
|
||||
ankerl::unordered_dense::map<CbufWordKey, u32, CbufWordKeyHash> cbuf_word_cache;
|
||||
ankerl::unordered_dense::map<HandleKey, u32, HandleKeyHash> handle_cache;
|
||||
ankerl::unordered_dense::map<const IR::Inst*, ConstBufferAddr> track_cache;
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -390,7 +390,7 @@ private:
|
|||
std::optional<Node> return_label) {
|
||||
Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false}, &root_stmt)};
|
||||
Tree& root{root_stmt.children};
|
||||
std::unordered_map<Flow::Block*, Node> local_labels;
|
||||
ankerl::unordered_dense::map<Flow::Block*, Node> local_labels;
|
||||
local_labels.reserve(function.blocks.size());
|
||||
|
||||
for (Flow::Block& block : function.blocks) {
|
||||
|
|
|
|||
|
|
@ -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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -16,7 +19,7 @@
|
|||
#include <deque>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -50,8 +53,8 @@ struct IndirectBranchVariable {
|
|||
auto operator<=>(const IndirectBranchVariable&) const noexcept = default;
|
||||
};
|
||||
|
||||
using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag,
|
||||
OverflowFlagTag, GotoVariable, IndirectBranchVariable>;
|
||||
using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, OverflowFlagTag, GotoVariable, IndirectBranchVariable>;
|
||||
// TODO: majority of these require stable iterators, test with XC beforehand
|
||||
using ValueMap = std::unordered_map<IR::Block*, IR::Value>;
|
||||
|
||||
struct DefTable {
|
||||
|
|
@ -112,6 +115,7 @@ struct DefTable {
|
|||
}
|
||||
|
||||
std::array<ValueMap, IR::NUM_USER_PREDS> preds;
|
||||
// TODO: Requires stable iterators
|
||||
std::unordered_map<u32, ValueMap> goto_vars;
|
||||
ValueMap indirect_branch_var;
|
||||
ValueMap zero_flag;
|
||||
|
|
@ -165,7 +169,8 @@ public:
|
|||
|
||||
template <typename Type>
|
||||
IR::Value ReadVariable(Type variable, IR::Block* root_block) {
|
||||
boost::container::small_vector<ReadState<Type>, 64> stack{
|
||||
// TODO: Windows commits sodoku if you use small_vector
|
||||
std::vector<ReadState<Type>> stack{
|
||||
ReadState<Type>(nullptr),
|
||||
ReadState<Type>(root_block),
|
||||
};
|
||||
|
|
@ -295,6 +300,7 @@ private:
|
|||
return same;
|
||||
}
|
||||
|
||||
// TODO: Windows dies with stack exhaustion?
|
||||
std::unordered_map<IR::Block*, std::map<Variant, IR::Inst*>> incomplete_phis;
|
||||
DefTable current_def;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <tuple>
|
||||
#include <limits>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -8,7 +11,7 @@
|
|||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
|
@ -36,7 +39,7 @@ public:
|
|||
|
||||
private:
|
||||
mutable std::mutex mutex;
|
||||
std::unordered_map<std::thread::id, u32> ids;
|
||||
ankerl::unordered_dense::map<std::thread::id, u32> ids;
|
||||
};
|
||||
|
||||
class TestControl1 {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
|
@ -65,7 +65,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<u64, int> page_table;
|
||||
ankerl::unordered_dense::map<u64, int> page_table;
|
||||
std::vector<std::tuple<DAddr, u64, int>> calls;
|
||||
size_t update_calls = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include <deque>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
|
||||
#include "common/alignment.h"
|
||||
|
|
@ -257,7 +257,7 @@ private:
|
|||
std::array<Manager*, NUM_HIGH_PAGES> top_tier{};
|
||||
std::deque<std::array<Manager, MANAGER_POOL_SIZE>> manager_pool;
|
||||
std::deque<Manager*> free_managers;
|
||||
std::unordered_set<u32> cached_pages;
|
||||
ankerl::unordered_dense::set<u32> cached_pages;
|
||||
DeviceTracker* device_tracker = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
@ -88,14 +88,14 @@ protected:
|
|||
|
||||
std::deque<P> channel_storage;
|
||||
std::deque<size_t> free_channel_ids;
|
||||
std::unordered_map<s32, size_t> channel_map;
|
||||
ankerl::unordered_dense::map<s32, size_t> channel_map;
|
||||
std::vector<size_t> active_channel_ids;
|
||||
struct AddressSpaceRef {
|
||||
size_t ref_count;
|
||||
size_t storage_id;
|
||||
Tegra::MemoryManager* gpu_memory;
|
||||
};
|
||||
std::unordered_map<size_t, AddressSpaceRef> address_spaces;
|
||||
ankerl::unordered_dense::map<size_t, AddressSpaceRef> address_spaces;
|
||||
mutable std::mutex config_mutex;
|
||||
|
||||
virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue