mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-24 10:36:55 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e85c23070 | |||
| 36b1b23d40 | |||
| 08ba001529 | |||
| 10a75d80eb | |||
| 8ec648d663 | |||
| ebedba1f2c | |||
| 8ebbdc9bee | |||
| 0495255867 | |||
| 7be3fc3ad5 | |||
| a4276b598b | |||
| 122afb04f7 | |||
| ea3de6ee81 | |||
| 32c76f1e0a | |||
| bce8e0aa6d | |||
| a4864176f5 | |||
| 1f2aa7e40b | |||
| ce8c754eb3 | |||
| 50e59cf09d | |||
| 5caa0bd567 | |||
| d93039fb42 | |||
| 17463f7b0d | |||
| 0ebf1ff07c | |||
| c0c0af6085 | |||
| a8589321bc | |||
| ce864eac38 | |||
| 4a3a2c0a11 | |||
| 42636ba127 |
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/libs/cobalt/include/boost/cobalt/concepts.hpp b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||||
|
index d49f2ec..a9bdb80 100644
|
||||||
|
--- a/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||||
|
+++ b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
||||||
|
@@ -62,7 +62,7 @@ struct enable_awaitables
|
||||||
|
template <typename T>
|
||||||
|
concept with_get_executor = requires (T& t)
|
||||||
|
{
|
||||||
|
- {t.get_executor()} -> asio::execution::executor;
|
||||||
|
+ t.get_executor();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
+7
-3
@@ -417,9 +417,12 @@ AddJsonPackage(boost)
|
|||||||
set(BOOST_NO_HEADERS ${Boost_ADDED})
|
set(BOOST_NO_HEADERS ${Boost_ADDED})
|
||||||
|
|
||||||
if (Boost_ADDED)
|
if (Boost_ADDED)
|
||||||
add_compile_definitions(YUZU_BOOST_v1)
|
if (MSVC OR ANDROID)
|
||||||
|
add_compile_definitions(YUZU_BOOST_v1)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT MSVC OR CXX_CLANG)
|
if (NOT MSVC OR CXX_CLANG)
|
||||||
# solaris sucks
|
# boost sucks
|
||||||
if (SOLARIS)
|
if (SOLARIS)
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
|
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
|
||||||
endif()
|
endif()
|
||||||
@@ -428,7 +431,8 @@ if (Boost_ADDED)
|
|||||||
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
||||||
target_compile_options(boost_asio INTERFACE
|
target_compile_options(boost_asio INTERFACE
|
||||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -6,19 +6,22 @@
|
|||||||
"version": "v0.19.0"
|
"version": "v0.19.0"
|
||||||
},
|
},
|
||||||
"boost": {
|
"boost": {
|
||||||
"artifact": "boost-%VERSION%.tar.zst",
|
"artifact": "%VERSION%-cmake.tar.xz",
|
||||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||||
"hash": "681d97be4386767662e230b2e7c9754d45e709cc5db4e747c23c27d1f5b0e87770bdf19b0bc44dcb0e8349324887bbb8fffb432098f4d0ce74a5043021a90afc",
|
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||||
"min_version": "1.57",
|
"min_version": "1.57",
|
||||||
"package": "Boost",
|
"package": "Boost",
|
||||||
"repo": "eden-emulator/ext-boost",
|
"patches": [
|
||||||
"version": "1.92.0"
|
"0001-clang-cl.patch"
|
||||||
|
],
|
||||||
|
"repo": "boostorg/boost",
|
||||||
|
"version": "boost-1.90.0"
|
||||||
},
|
},
|
||||||
"boost_headers": {
|
"boost_headers": {
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"hash": "c5fa2cd72f6e6666b7963b97bc359c75284b8fb540c30f3629a028b85270c9bc66c8a051383964f2bd4c1e005a4691593d15696e7ef39ea87cf6cff9e5691fb2",
|
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
||||||
"repo": "boostorg/headers",
|
"repo": "boostorg/headers",
|
||||||
"version": "boost-1.91.0"
|
"version": "boost-1.90.0"
|
||||||
},
|
},
|
||||||
"catch2": {
|
"catch2": {
|
||||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||||
|
|||||||
Vendored
+579
-624
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+503
-548
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+508
-550
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
Vendored
+527
-573
File diff suppressed because it is too large
Load Diff
Vendored
+502
-547
File diff suppressed because it is too large
Load Diff
+1
@@ -27,6 +27,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
||||||
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
||||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||||
|
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
|
||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
|
|||||||
+7
@@ -785,6 +785,13 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.renderer_asynchronous_shaders_description
|
descriptionId = R.string.renderer_asynchronous_shaders_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.RENDERER_UNIFIED_MEMORY,
|
||||||
|
titleId = R.string.renderer_unified_memory,
|
||||||
|
descriptionId = R.string.renderer_unified_memory_description
|
||||||
|
)
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
SingleChoiceSetting(
|
SingleChoiceSetting(
|
||||||
IntSetting.FAST_GPU_TIME,
|
IntSetting.FAST_GPU_TIME,
|
||||||
|
|||||||
+1
@@ -558,6 +558,7 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||||
add(BooleanSetting.EMULATE_BGR565.key)
|
add(BooleanSetting.EMULATE_BGR565.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
|
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
|
||||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
||||||
|
|||||||
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
|||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jupdatePath) {
|
jstring jupdatePath) {
|
||||||
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||||
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
|||||||
for (const auto& item : nsp->GetNCAs()) {
|
for (const auto& item : nsp->GetNCAs()) {
|
||||||
for (const auto& nca_details : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||||
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
||||||
if (update_id == program_id) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,7 +289,6 @@
|
|||||||
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
|
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
|
||||||
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
|
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
|
||||||
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
|
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
|
||||||
<string name="post_processing_add">Añadir efecto</string>
|
|
||||||
<string name="post_processing_open_list">Abrir lista</string>
|
<string name="post_processing_open_list">Abrir lista</string>
|
||||||
<string name="post_processing_close_list">Cerrar lista</string>
|
<string name="post_processing_close_list">Cerrar lista</string>
|
||||||
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
||||||
|
|||||||
@@ -318,13 +318,6 @@
|
|||||||
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
||||||
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
||||||
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
|
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
|
||||||
<string name="frame_gen_on">Вкл.</string>
|
|
||||||
<string name="frame_gen_off">Выкл.</string>
|
|
||||||
<string name="frame_gen_fixed">Фикс.</string>
|
|
||||||
<string name="frame_gen_flow_auto">Авто</string>
|
|
||||||
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
|
|
||||||
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
|
|
||||||
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
|
|
||||||
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
|
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
|
||||||
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
||||||
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
|
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
|
||||||
@@ -335,14 +328,12 @@
|
|||||||
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
|
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
|
||||||
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
||||||
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
|
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
|
||||||
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
|
|
||||||
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
||||||
<string name="lossless_scaling_install">Установить Lossless.dll</string>
|
<string name="lossless_scaling_install">Установить Lossless.dll</string>
|
||||||
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
|
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
|
||||||
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
||||||
<string name="frame_generation_support">Генерация кадров</string>
|
<string name="frame_generation_support">Генерация кадров</string>
|
||||||
<string name="frame_generation_supported">Поддерживается</string>
|
<string name="frame_generation_supported">Поддерживается</string>
|
||||||
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
|
|
||||||
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
|
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
|
||||||
<string name="lossless_scaling_installed">Установлена</string>
|
<string name="lossless_scaling_installed">Установлена</string>
|
||||||
<string name="lossless_scaling_not_installed">Не установлена</string>
|
<string name="lossless_scaling_not_installed">Не установлена</string>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
|
|
||||||
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
|
|
||||||
<string name="notice_notification_channel_name">错误与注意事项</string>
|
<string name="notice_notification_channel_name">错误与注意事项</string>
|
||||||
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
||||||
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
||||||
@@ -291,25 +290,6 @@
|
|||||||
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
||||||
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
||||||
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
|
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
|
||||||
<string name="post_processing">后处理效果</string>
|
|
||||||
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
|
|
||||||
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
|
|
||||||
<string name="post_processing_add">添加效果</string>
|
|
||||||
<string name="post_processing_remove">移除</string>
|
|
||||||
<string name="post_processing_open_list">打开列表</string>
|
|
||||||
<string name="post_processing_close_list">关闭列表</string>
|
|
||||||
<string name="post_processing_remove_all">移除效果</string>
|
|
||||||
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
|
|
||||||
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
|
|
||||||
<string name="post_processing_presets">预设</string>
|
|
||||||
<string name="post_processing_preset_new">新建预设</string>
|
|
||||||
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
|
|
||||||
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
|
|
||||||
<string name="post_processing_preset_none">无预设</string>
|
|
||||||
<string name="post_processing_preset_delete">删除预设</string>
|
|
||||||
<string name="post_processing_preset_reset">重置预设值</string>
|
|
||||||
<string name="post_processing_reset">重置为默认值</string>
|
|
||||||
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
|
|
||||||
<string name="frame_gen">帧生成</string>
|
<string name="frame_gen">帧生成</string>
|
||||||
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
||||||
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
|
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
|
||||||
@@ -324,13 +304,6 @@
|
|||||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||||
<string name="frame_gen_on">开</string>
|
|
||||||
<string name="frame_gen_off">关</string>
|
|
||||||
<string name="frame_gen_fixed">固定</string>
|
|
||||||
<string name="frame_gen_flow_auto">自动</string>
|
|
||||||
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
|
|
||||||
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
|
|
||||||
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
|
|
||||||
<string name="frame_gen_queue_target">帧队列目标</string>
|
<string name="frame_gen_queue_target">帧队列目标</string>
|
||||||
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
||||||
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
|
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
|
||||||
@@ -341,14 +314,12 @@
|
|||||||
<string name="frame_gen_flow_scale">运动预估分辨率</string>
|
<string name="frame_gen_flow_scale">运动预估分辨率</string>
|
||||||
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
||||||
<string name="frame_gen_unsupported">帧生成不可用</string>
|
<string name="frame_gen_unsupported">帧生成不可用</string>
|
||||||
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
|
|
||||||
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
||||||
<string name="lossless_scaling_install">安装 Lossless.dll</string>
|
<string name="lossless_scaling_install">安装 Lossless.dll</string>
|
||||||
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
|
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
|
||||||
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
||||||
<string name="frame_generation_support">帧生成</string>
|
<string name="frame_generation_support">帧生成</string>
|
||||||
<string name="frame_generation_supported">支持</string>
|
<string name="frame_generation_supported">支持</string>
|
||||||
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
|
|
||||||
<string name="lossless_scaling">无损缩放</string>
|
<string name="lossless_scaling">无损缩放</string>
|
||||||
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
||||||
<string name="lossless_scaling_installed">已安装</string>
|
<string name="lossless_scaling_installed">已安装</string>
|
||||||
@@ -640,11 +611,6 @@
|
|||||||
<string name="log">日志记录</string>
|
<string name="log">日志记录</string>
|
||||||
<string name="flush_by_line">按行刷新调试日志</string>
|
<string name="flush_by_line">按行刷新调试日志</string>
|
||||||
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
|
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
|
||||||
<string name="extended_logging">开启扩展日志</string>
|
|
||||||
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
|
|
||||||
<string name="log_filter">日志筛选器</string>
|
|
||||||
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
|
|
||||||
|
|
||||||
<!-- GPU Logging strings -->
|
<!-- GPU Logging strings -->
|
||||||
<string name="gpu_logging_header">GPU 日志</string>
|
<string name="gpu_logging_header">GPU 日志</string>
|
||||||
<string name="gpu_log_level">日志等级</string>
|
<string name="gpu_log_level">日志等级</string>
|
||||||
@@ -928,8 +894,6 @@
|
|||||||
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
||||||
|
|
||||||
<string name="loader_requires_firmware">游戏需要固件</string>
|
<string name="loader_requires_firmware">游戏需要固件</string>
|
||||||
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
|
|
||||||
|
|
||||||
<!-- Intent Launch strings -->
|
<!-- Intent Launch strings -->
|
||||||
<string name="searching_for_game">正在搜索游戏...</string>
|
<string name="searching_for_game">正在搜索游戏...</string>
|
||||||
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
||||||
|
|||||||
@@ -229,7 +229,7 @@
|
|||||||
<string name="add_directory_success">遊戲目錄新增成功</string>
|
<string name="add_directory_success">遊戲目錄新增成功</string>
|
||||||
<string name="enable_update_checks">檢查更新</string>
|
<string name="enable_update_checks">檢查更新</string>
|
||||||
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
|
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
|
||||||
<string name="update_available">發現新版本</string>
|
<string name="update_available">偵測到新版本</string>
|
||||||
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
|
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
|
||||||
<string name="downloading_update">下載更新中</string>
|
<string name="downloading_update">下載更新中</string>
|
||||||
<string name="update_download_failed">更新下載失敗</string>
|
<string name="update_download_failed">更新下載失敗</string>
|
||||||
@@ -1020,40 +1020,9 @@
|
|||||||
<string name="memory_6gb">6GB (不穩定)</string>
|
<string name="memory_6gb">6GB (不穩定)</string>
|
||||||
<string name="memory_8gb">8GB (不穩定)</string>
|
<string name="memory_8gb">8GB (不穩定)</string>
|
||||||
|
|
||||||
<!-- CPU clock levels -->
|
|
||||||
<string name="clock_normal">正常</string>
|
|
||||||
<string name="clock_boost">加速</string>
|
|
||||||
<string name="clock_fast">超頻</string>
|
|
||||||
|
|
||||||
<!-- GPU clock levels -->
|
|
||||||
<string name="fast_gpu_normal">正常</string>
|
|
||||||
<string name="fast_gpu_medium">加速</string>
|
|
||||||
<string name="fast_gpu_high">超頻</string>
|
|
||||||
|
|
||||||
<!-- GPU swizzle texture size -->
|
|
||||||
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
|
|
||||||
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
|
|
||||||
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
|
|
||||||
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
|
|
||||||
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
|
|
||||||
|
|
||||||
<!-- GPU swizzle streams -->
|
|
||||||
<string name="gpu_swizzle_verylow">極小(4MB)</string>
|
|
||||||
<string name="gpu_swizzle_low">小(8MB)</string>
|
|
||||||
<string name="gpu_swizzle_normal">正常(16MB)</string>
|
|
||||||
<string name="gpu_swizzle_medium">中(32MB)</string>
|
|
||||||
<string name="gpu_swizzle_high">大(64MB)</string>
|
|
||||||
|
|
||||||
<!-- GPU swizzle chunks -->
|
|
||||||
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
|
|
||||||
<string name="gpu_swizzlechunk_low">小(64)</string>
|
|
||||||
<string name="gpu_swizzlechunk_normal">正常(128)</string>
|
|
||||||
<string name="gpu_swizzlechunk_medium">中(256)</string>
|
|
||||||
<string name="gpu_swizzlechunk_high">大(512)</string>
|
|
||||||
|
|
||||||
<!-- Temperature Units -->
|
<!-- Temperature Units -->
|
||||||
<string name="temperature_celsius">攝氏</string>
|
<string name="temperature_celsius">攝氏度</string>
|
||||||
<string name="temperature_fahrenheit">華氏</string>
|
<string name="temperature_fahrenheit">華氏度</string>
|
||||||
|
|
||||||
<!-- Memory Sizes -->
|
<!-- Memory Sizes -->
|
||||||
<string name="memory_byte_shorthand">B</string>
|
<string name="memory_byte_shorthand">B</string>
|
||||||
@@ -1066,8 +1035,6 @@
|
|||||||
|
|
||||||
<string name="renderer_none">無</string>
|
<string name="renderer_none">無</string>
|
||||||
|
|
||||||
<!-- Renderer Accuracy -->
|
|
||||||
<string name="renderer_accuracy_low">效能</string>
|
|
||||||
<string name="renderer_accuracy_high">準確</string>
|
<string name="renderer_accuracy_high">準確</string>
|
||||||
|
|
||||||
<!-- DMA Accuracy -->
|
<!-- DMA Accuracy -->
|
||||||
|
|||||||
@@ -605,6 +605,8 @@
|
|||||||
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
||||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||||
|
<string name="renderer_unified_memory">Unified memory access</string>
|
||||||
|
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
|
||||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||||
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
||||||
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -19,11 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADSP::Shutdown() {
|
|
||||||
audio_renderer->Stop();
|
|
||||||
opus_decoder->Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
|
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
|
||||||
return *audio_renderer;
|
return *audio_renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
@@ -45,7 +45,6 @@ public:
|
|||||||
explicit ADSP(Core::System& system, Sink::Sink& sink);
|
explicit ADSP(Core::System& system, Sink::Sink& sink);
|
||||||
~ADSP() = default;
|
~ADSP() = default;
|
||||||
|
|
||||||
void Shutdown();
|
|
||||||
AudioRenderer::AudioRenderer& AudioRenderer();
|
AudioRenderer::AudioRenderer& AudioRenderer();
|
||||||
OpusDecoder::OpusDecoder& OpusDecoder();
|
OpusDecoder::OpusDecoder& OpusDecoder();
|
||||||
|
|
||||||
|
|||||||
@@ -34,19 +34,26 @@ void AudioRenderer::Start() {
|
|||||||
|
|
||||||
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
mailbox.Send(Direction::DSP, Message::InitializeOK);
|
||||||
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
|
||||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
|
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||||
|
"message response from ADSP!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Stop() {
|
void AudioRenderer::Stop() {
|
||||||
if (main_thread.joinable()) {
|
if (!running) {
|
||||||
main_thread.request_stop();
|
return;
|
||||||
mailbox.Send(Direction::DSP, Message::Shutdown);
|
|
||||||
if (mailbox.Receive(Direction::Host, main_thread.get_stop_token()) != Message::Shutdown) {
|
|
||||||
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
|
|
||||||
}
|
|
||||||
main_thread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mailbox.Send(Direction::DSP, Message::Shutdown);
|
||||||
|
if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
|
||||||
|
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
|
||||||
|
"message response from ADSP!");
|
||||||
|
}
|
||||||
|
main_thread.request_stop();
|
||||||
|
main_thread.join();
|
||||||
|
|
||||||
for (auto& stream : streams) {
|
for (auto& stream : streams) {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream->Stop();
|
stream->Stop();
|
||||||
@@ -54,6 +61,7 @@ void AudioRenderer::Stop() {
|
|||||||
stream = nullptr;
|
stream = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Signal() {
|
void AudioRenderer::Signal() {
|
||||||
@@ -61,12 +69,13 @@ void AudioRenderer::Signal() {
|
|||||||
Send(Direction::DSP, Message::Render);
|
Send(Direction::DSP, Message::Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::Wait(std::stop_token stop_token) {
|
void AudioRenderer::Wait() {
|
||||||
auto msg = mailbox.Receive(Direction::Host, stop_token);
|
auto msg = Receive(Direction::Host);
|
||||||
if (msg != Message::RenderResponse) {
|
if (msg != Message::RenderResponse) {
|
||||||
LOG_ERROR(Service_Audio,
|
LOG_ERROR(Service_Audio,
|
||||||
"Did not receive the expected render response from the AudioRenderer! Expected "
|
"Did not receive the expected render response from the AudioRenderer! Expected "
|
||||||
"{}, got {}", Message::RenderResponse, msg);
|
"{}, got {}",
|
||||||
|
Message::RenderResponse, msg);
|
||||||
}
|
}
|
||||||
PostDSPClearCommandBuffer();
|
PostDSPClearCommandBuffer();
|
||||||
}
|
}
|
||||||
@@ -138,9 +147,7 @@ void AudioRenderer::Main(std::stop_token stop_token) {
|
|||||||
constexpr u64 max_process_time{2'304'000ULL};
|
constexpr u64 max_process_time{2'304'000ULL};
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto msg = mailbox.Receive(Direction::DSP, stop_token);
|
auto msg{mailbox.Receive(Direction::DSP)};
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Message::Shutdown:
|
case Message::Shutdown:
|
||||||
mailbox.Send(Direction::Host, Message::Shutdown);
|
mailbox.Send(Direction::Host, Message::Shutdown);
|
||||||
|
|||||||
@@ -63,15 +63,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
void NotifyShutdown();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the AudioRenderer.
|
* Stop the AudioRenderer.
|
||||||
*/
|
*/
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
void Signal();
|
void Signal();
|
||||||
void Wait(std::stop_token stop_token);
|
void Wait();
|
||||||
|
|
||||||
void Send(Direction dir, u32 message);
|
void Send(Direction dir, u32 message);
|
||||||
u32 Receive(Direction dir);
|
u32 Receive(Direction dir);
|
||||||
@@ -104,6 +102,8 @@ private:
|
|||||||
Mailbox mailbox;
|
Mailbox mailbox;
|
||||||
/// Main thread
|
/// Main thread
|
||||||
std::jthread main_thread{};
|
std::jthread main_thread{};
|
||||||
|
/// The current state
|
||||||
|
std::atomic<bool> running{};
|
||||||
/// Shared memory of input command buffers, set by host, read by DSP
|
/// Shared memory of input command buffers, set by host, read by DSP
|
||||||
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
|
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
|
||||||
/// The command lists to process
|
/// The command lists to process
|
||||||
|
|||||||
@@ -41,16 +41,19 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
if (main_thread.joinable()) {
|
if (!running) {
|
||||||
// Shutdown the thread
|
|
||||||
Send(Direction::DSP, Message::Shutdown);
|
|
||||||
main_thread.request_stop();
|
|
||||||
auto msg = Receive(Direction::Host, main_thread.get_stop_token());
|
|
||||||
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
|
|
||||||
main_thread.join();
|
|
||||||
} else {
|
|
||||||
init_thread.request_stop();
|
init_thread.request_stop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown the thread
|
||||||
|
Send(Direction::DSP, Message::Shutdown);
|
||||||
|
auto msg = Receive(Direction::Host);
|
||||||
|
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
|
||||||
|
Message::ShutdownOK, msg);
|
||||||
|
main_thread.request_stop();
|
||||||
|
main_thread.join();
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Send(Direction dir, u32 message) {
|
void OpusDecoder::Send(Direction dir, u32 message) {
|
||||||
@@ -70,6 +73,7 @@ void OpusDecoder::Init(std::stop_token stop_token) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
|
||||||
|
running = true;
|
||||||
Send(Direction::Host, Message::StartOK);
|
Send(Direction::Host, Message::StartOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,15 +82,12 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
auto msg = Receive(Direction::DSP, stop_token);
|
auto msg = Receive(Direction::DSP, stop_token);
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Message::Shutdown:
|
case Shutdown:
|
||||||
Send(Direction::Host, Message::ShutdownOK);
|
Send(Direction::Host, Message::ShutdownOK);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case Message::GetWorkBufferSize: {
|
case GetWorkBufferSize: {
|
||||||
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
|
|
||||||
ASSERT(IsValidChannelCount(channel_count));
|
ASSERT(IsValidChannelCount(channel_count));
|
||||||
@@ -95,7 +96,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
Send(Direction::Host, Message::GetWorkBufferSizeOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::InitializeDecodeObject: {
|
case InitializeDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
@@ -112,7 +113,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
Send(Direction::Host, Message::InitializeDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::ShutdownDecodeObject: {
|
case ShutdownDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::DecodeInterleaved: {
|
case DecodeInterleaved: {
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
@@ -160,19 +161,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::DecodeInterleavedOK);
|
Send(Direction::Host, Message::DecodeInterleavedOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::MapMemory: {
|
case MapMemory: {
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
Send(Direction::Host, Message::MapMemoryOK);
|
Send(Direction::Host, Message::MapMemoryOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::UnmapMemory: {
|
case UnmapMemory: {
|
||||||
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
Send(Direction::Host, Message::UnmapMemoryOK);
|
Send(Direction::Host, Message::UnmapMemoryOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::GetWorkBufferSizeForMultiStream: {
|
case GetWorkBufferSizeForMultiStream: {
|
||||||
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
|
||||||
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
|
||||||
|
|
||||||
@@ -183,7 +184,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::InitializeMultiStreamDecodeObject: {
|
case InitializeMultiStreamDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
auto buffer_size = shared_memory->host_send_data[1];
|
auto buffer_size = shared_memory->host_send_data[1];
|
||||||
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
|
||||||
@@ -210,7 +211,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::ShutdownMultiStreamDecodeObject: {
|
case ShutdownMultiStreamDecodeObject: {
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Message::DecodeInterleavedForMultiStream: {
|
case DecodeInterleavedForMultiStream: {
|
||||||
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
auto start_time = system.CoreTiming().GetGlobalTimeUs();
|
||||||
|
|
||||||
auto buffer = shared_memory->host_send_data[0];
|
auto buffer = shared_memory->host_send_data[0];
|
||||||
@@ -265,15 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpusDecoder::Shutdown() {
|
|
||||||
if (init_thread.joinable()) {
|
|
||||||
init_thread.request_stop();
|
|
||||||
init_thread.join(); //must explicitly wait for init to join
|
|
||||||
}
|
|
||||||
if (main_thread.joinable()) {
|
|
||||||
main_thread.request_stop();
|
|
||||||
Send(Direction::DSP, Message::Shutdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore::ADSP::OpusDecoder
|
} // namespace AudioCore::ADSP::OpusDecoder
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
~OpusDecoder();
|
~OpusDecoder();
|
||||||
|
|
||||||
bool IsRunning() const noexcept {
|
bool IsRunning() const noexcept {
|
||||||
return main_thread.joinable();
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Send(Direction dir, u32 message);
|
void Send(Direction dir, u32 message);
|
||||||
@@ -67,8 +67,6 @@ public:
|
|||||||
shared_memory = &shared_memory_;
|
shared_memory = &shared_memory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
|
||||||
@@ -87,6 +85,8 @@ private:
|
|||||||
std::jthread init_thread{};
|
std::jthread init_thread{};
|
||||||
/// Main thread
|
/// Main thread
|
||||||
std::jthread main_thread{};
|
std::jthread main_thread{};
|
||||||
|
/// The current state
|
||||||
|
bool running{};
|
||||||
/// Structure shared with the host, input data set by the host before sending a mailbox message,
|
/// Structure shared with the host, input data set by the host before sending a mailbox message,
|
||||||
/// and the responses are written back by the OpusDecoder.
|
/// and the responses are written back by the OpusDecoder.
|
||||||
SharedMemory* shared_memory{};
|
SharedMemory* shared_memory{};
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ void AudioCore::CreateSinks() {
|
|||||||
|
|
||||||
void AudioCore::Shutdown() {
|
void AudioCore::Shutdown() {
|
||||||
audio_manager->Shutdown();
|
audio_manager->Shutdown();
|
||||||
adsp->Shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager& AudioCore::GetAudioManager() {
|
AudioManager& AudioCore::GetAudioManager() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
|
|||||||
std::unique_lock l{events.GetAudioEventLock()};
|
std::unique_lock l{events.GetAudioEventLock()};
|
||||||
events.ClearEvents();
|
events.ClearEvents();
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
const auto timed_out = events.Wait(l, std::chrono::seconds{2});
|
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -35,9 +35,9 @@ AudioManager::AudioManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioManager::Shutdown() {
|
void AudioManager::Shutdown() {
|
||||||
|
events.SetAudioEvent(Event::Type::Max, true);
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
events.SetAudioEvent(Event::Type::Max, true);
|
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ class AudioManager {
|
|||||||
public:
|
public:
|
||||||
explicit AudioManager();
|
explicit AudioManager();
|
||||||
|
|
||||||
/// @brief Shutdown the audio manager.
|
/**
|
||||||
|
* Shutdown the audio manager.
|
||||||
|
*/
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
|
|||||||
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
|
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
|
||||||
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
|
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
|
||||||
|
|
||||||
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
|
if (in_use == buffer_params->in_use && !buffer_unmapped) {
|
||||||
error_info.error_code = ResultSuccess;
|
error_info.error_code = ResultSuccess;
|
||||||
error_info.address = CpuAddr(0);
|
error_info.address = CpuAddr(0);
|
||||||
out_status.writeOffset = current_state->last_pos2;
|
out_status.writeOffset = current_state->last_pos2;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -31,10 +31,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct DeviceInParameter {
|
struct DeviceInParameter {
|
||||||
/* 0x000 */ u8 name[0x100];
|
/* 0x000 */ char name[0x100];
|
||||||
/* 0x100 */ u32 input_count;
|
/* 0x100 */ u32 input_count;
|
||||||
/* 0x104 */ std::array<s8, MaxChannels> inputs;
|
/* 0x104 */ std::array<s8, MaxChannels> inputs;
|
||||||
/* 0x10A */ u8 unk10A[0x1];
|
/* 0x10A */ char unk10A[0x1];
|
||||||
/* 0x10B */ bool downmix_enabled;
|
/* 0x10B */ bool downmix_enabled;
|
||||||
/* 0x10C */ std::array<f32, 4> downmix_coeff;
|
/* 0x10C */ std::array<f32, 4> downmix_coeff;
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,7 @@ public:
|
|||||||
struct DeviceState {
|
struct DeviceState {
|
||||||
/* 0x00 */ UpsamplerInfo* upsampler_info;
|
/* 0x00 */ UpsamplerInfo* upsampler_info;
|
||||||
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
|
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
|
||||||
/* 0x18 */ u8 unk18[0x18];
|
/* 0x18 */ char unk18[0x18];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
|
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ public:
|
|||||||
/* 0x14 */ u32 previous_pos;
|
/* 0x14 */ u32 previous_pos;
|
||||||
/* 0x18 */ SampleFormat format;
|
/* 0x18 */ SampleFormat format;
|
||||||
/* 0x1C */ std::array<s8, MaxChannels> inputs;
|
/* 0x1C */ std::array<s8, MaxChannels> inputs;
|
||||||
/* 0x22 */ u8 in_use;
|
/* 0x22 */ bool in_use;
|
||||||
/* 0x23 */ u8 unk23[0x5];
|
/* 0x23 */ char unk23[0x5];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CircularBufferInParameter) == 0x28,
|
static_assert(sizeof(CircularBufferInParameter) == 0x28,
|
||||||
"CircularBufferInParameter has the wrong size!");
|
"CircularBufferInParameter has the wrong size!");
|
||||||
@@ -65,16 +65,16 @@ public:
|
|||||||
/* 0x00 */ u32 last_pos2;
|
/* 0x00 */ u32 last_pos2;
|
||||||
/* 0x04 */ s32 current_pos;
|
/* 0x04 */ s32 current_pos;
|
||||||
/* 0x08 */ u32 last_pos;
|
/* 0x08 */ u32 last_pos;
|
||||||
/* 0x0C */ u8 unk0C[0x4];
|
/* 0x0C */ char unk0C[0x4];
|
||||||
/* 0x10 */ AddressInfo address_info;
|
/* 0x10 */ AddressInfo address_info;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
|
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
|
||||||
|
|
||||||
struct InParameter {
|
struct InParameter {
|
||||||
/* 0x000 */ Type type;
|
/* 0x000 */ Type type;
|
||||||
/* 0x001 */ u8 in_use;
|
/* 0x001 */ bool in_use;
|
||||||
/* 0x004 */ u32 node_id;
|
/* 0x004 */ u32 node_id;
|
||||||
/* 0x008 */ u8 unk08[0x18];
|
/* 0x008 */ char unk08[0x18];
|
||||||
union {
|
union {
|
||||||
/* 0x020 */ DeviceInParameter device;
|
/* 0x020 */ DeviceInParameter device;
|
||||||
/* 0x020 */ CircularBufferInParameter circular_buffer;
|
/* 0x020 */ CircularBufferInParameter circular_buffer;
|
||||||
@@ -84,7 +84,7 @@ public:
|
|||||||
|
|
||||||
struct OutStatus {
|
struct OutStatus {
|
||||||
/* 0x00 */ u32 writeOffset;
|
/* 0x00 */ u32 writeOffset;
|
||||||
/* 0x04 */ u8 unk04[0x1C];
|
/* 0x04 */ char unk04[0x1C];
|
||||||
}; // size == 0x20
|
}; // size == 0x20
|
||||||
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
|
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
|
||||||
|
|
||||||
@@ -162,18 +162,19 @@ public:
|
|||||||
u8* GetParameter();
|
u8* GetParameter();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Type of this sink
|
||||||
|
Type type{Type::Invalid};
|
||||||
|
/// Is this sink in use?
|
||||||
|
bool in_use{};
|
||||||
|
/// Is this sink's buffer unmapped? Circular only
|
||||||
|
bool buffer_unmapped{};
|
||||||
|
/// Node id for this sink
|
||||||
|
u32 node_id{};
|
||||||
/// State buffer for this sink
|
/// State buffer for this sink
|
||||||
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
|
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
|
||||||
/// Parameter buffer for this sink
|
/// Parameter buffer for this sink
|
||||||
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{};
|
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
|
||||||
/// Type of this sink
|
parameter{};
|
||||||
Type type{Type::Invalid};
|
|
||||||
/// Node id for this sink
|
|
||||||
u32 node_id{};
|
|
||||||
/// Is this sink in use?
|
|
||||||
bool in_use : 1 = false;
|
|
||||||
/// Is this sink's buffer unmapped? Circular only
|
|
||||||
bool buffer_unmapped : 1 = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -100,9 +100,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||||
: core{core_}
|
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
|
||||||
, adsp_rendered_event{adsp_rendered_event_}
|
adsp_rendered_event{adsp_rendered_event_} {}
|
||||||
{}
|
|
||||||
|
|
||||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||||
@@ -407,7 +406,7 @@ void System::Finalize() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsActive()) {
|
if (active) {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,12 +433,14 @@ void System::Start() {
|
|||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
frames_elapsed = 0;
|
frames_elapsed = 0;
|
||||||
state = State::Started;
|
state = State::Started;
|
||||||
|
active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::Stop() {
|
void System::Stop() {
|
||||||
{
|
{
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
state = State::Stopped;
|
state = State::Stopped;
|
||||||
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execution_mode == ExecutionMode::Auto) {
|
if (execution_mode == ExecutionMode::Auto) {
|
||||||
@@ -479,7 +480,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer,
|
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
|
||||||
memory_pool_count);
|
memory_pool_count);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
||||||
@@ -581,16 +582,16 @@ u32 System::GetRenderingDevice() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsActive() const {
|
bool System::IsActive() const {
|
||||||
return state == State::Started;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SendCommandToDsp() {
|
void System::SendCommandToDsp() {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
if (IsActive()) {
|
if (active) {
|
||||||
terminate_event.Reset();
|
terminate_event.Reset();
|
||||||
const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id);
|
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
|
||||||
u64 command_size{0};
|
u64 command_size{0};
|
||||||
|
|
||||||
if (remaining_command_count) {
|
if (remaining_command_count) {
|
||||||
@@ -617,17 +618,16 @@ void System::SendCommandToDsp() {
|
|||||||
auto time_limit{
|
auto time_limit{
|
||||||
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
|
||||||
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
|
||||||
core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id,
|
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
|
||||||
translated_addr, command_size,
|
applet_resource_user_id, process_handle,
|
||||||
time_limit, applet_resource_user_id,
|
reset_command_buffers);
|
||||||
process_handle, reset_command_buffers);
|
|
||||||
reset_command_buffers = false;
|
reset_command_buffers = false;
|
||||||
command_buffer_size = command_size;
|
command_buffer_size = command_size;
|
||||||
if (remaining_command_count == 0) {
|
if (remaining_command_count == 0) {
|
||||||
adsp_rendered_event->Signal(core.Kernel());
|
adsp_rendered_event->Signal(core.Kernel());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id);
|
audio_renderer.ClearRemainCommandCount(session_id);
|
||||||
terminate_event.Set();
|
terminate_event.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -738,7 +738,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
|||||||
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
||||||
total_ticks_elapsed += end_time - start_time;
|
total_ticks_elapsed += end_time - start_time;
|
||||||
num_command_lists_generated++;
|
num_command_lists_generated++;
|
||||||
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
|
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
|
||||||
frames_elapsed++;
|
frames_elapsed++;
|
||||||
|
|
||||||
return command_buffer.size;
|
return command_buffer.size;
|
||||||
|
|||||||
@@ -219,6 +219,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& core;
|
Core::System& core;
|
||||||
|
/// Reference to the ADSP's AudioRenderer for communication
|
||||||
|
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
||||||
/// Is this system initialized?
|
/// Is this system initialized?
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
/// Is this system currently active?
|
/// Is this system currently active?
|
||||||
|
|||||||
@@ -11,70 +11,73 @@
|
|||||||
#include "audio_core/renderer/system_manager.h"
|
#include "audio_core/renderer/system_manager.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/kernel/k_scheduler.h"
|
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
SystemManager::SystemManager(Core::System& core_)
|
SystemManager::SystemManager(Core::System& core_)
|
||||||
: core{core_}
|
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemManager::~SystemManager() {
|
SystemManager::~SystemManager() {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::InitializeUnsafe() {
|
void SystemManager::InitializeUnsafe() {
|
||||||
if (!thread.joinable()) {
|
if (!active) {
|
||||||
core.AudioCore().ADSP().AudioRenderer().Start();
|
active = true;
|
||||||
thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() {
|
audio_renderer.Start();
|
||||||
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
auto const stop_token = thread.get_stop_token();
|
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
||||||
while (!stop_token.stop_requested()) {
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||||
|
while (active && !stop_token.stop_requested()) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock l{mutex1};
|
||||||
for (auto system : systems)
|
for (auto system : systems)
|
||||||
system->SendCommandToDsp();
|
system->SendCommandToDsp();
|
||||||
}
|
}
|
||||||
audio_renderer.Signal();
|
audio_renderer.Signal();
|
||||||
audio_renderer.Wait(stop_token);
|
audio_renderer.Wait();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::Stop() {
|
void SystemManager::Stop() {
|
||||||
core.AudioCore().ADSP().AudioRenderer().Stop();
|
if (active) {
|
||||||
if (thread.joinable()) {
|
active = false;
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
thread.join();
|
thread.join();
|
||||||
|
audio_renderer.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Add(System& system_) {
|
bool SystemManager::Add(System& system_) {
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock l2{mutex2};
|
||||||
if (systems.size() >= MaxRendererSessions) {
|
if (systems.size() + 1 > MaxRendererSessions) {
|
||||||
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
systems.push_back(&system_);
|
{
|
||||||
if (!systems.empty()) {
|
std::scoped_lock l{mutex1};
|
||||||
InitializeUnsafe();
|
if (systems.empty())
|
||||||
|
InitializeUnsafe();
|
||||||
}
|
}
|
||||||
|
systems.push_back(&system_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Remove(System& system_) {
|
bool SystemManager::Remove(System& system_) {
|
||||||
std::scoped_lock lk{mutex};
|
std::scoped_lock l2{mutex2};
|
||||||
if (systems.remove(&system_) == 0) {
|
{
|
||||||
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
std::scoped_lock l{mutex1};
|
||||||
return false;
|
if (systems.remove(&system_) == 0) {
|
||||||
|
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (systems.empty()) {
|
|
||||||
|
if (systems.empty())
|
||||||
Stop();
|
Stop();
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,8 +75,14 @@ private:
|
|||||||
std::list<System*> systems{};
|
std::list<System*> systems{};
|
||||||
/// Main worker thread for generating command lists
|
/// Main worker thread for generating command lists
|
||||||
std::jthread thread;
|
std::jthread thread;
|
||||||
/// Mutex for the reading and adding/removing systems
|
/// Mutex for the systems
|
||||||
std::mutex mutex{};
|
std::mutex mutex1{};
|
||||||
|
/// Mutex for adding/removing systems
|
||||||
|
std::mutex mutex2{};
|
||||||
|
/// Is the system manager thread active?
|
||||||
|
std::atomic<bool> active{};
|
||||||
|
/// Reference to the ADSP's AudioRenderer for communication
|
||||||
|
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ add_library(
|
|||||||
socket_types.h
|
socket_types.h
|
||||||
sparse_large_vector.cpp
|
sparse_large_vector.cpp
|
||||||
sparse_large_vector.h
|
sparse_large_vector.h
|
||||||
|
spin_lock.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
steady_clock.cpp
|
steady_clock.cpp
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ private:
|
|||||||
if (read_index == producer.index.load(std::memory_order::acquire)) {
|
if (read_index == producer.index.load(std::memory_order::acquire)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if constexpr (Mode == PopMode::Wait) {
|
||||||
|
// Wait until the queue is not empty.
|
||||||
|
std::unique_lock lock{consumer.cv_mutex};
|
||||||
|
consumer.cv.wait(lock, [this, read_index] {
|
||||||
|
return read_index != producer.index.load(std::memory_order::acquire);
|
||||||
|
});
|
||||||
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
||||||
// Wait until the queue is not empty.
|
// Wait until the queue is not empty.
|
||||||
std::unique_lock lock{consumer.cv_mutex};
|
std::unique_lock lock{consumer.cv_mutex};
|
||||||
@@ -118,12 +124,6 @@ private:
|
|||||||
if (stop_token.stop_requested()) {
|
if (stop_token.stop_requested()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if constexpr (Mode == PopMode::Wait) {
|
|
||||||
// Wait until the queue is not empty.
|
|
||||||
std::unique_lock lock{consumer.cv_mutex};
|
|
||||||
consumer.cv.wait(lock, [this, read_index] {
|
|
||||||
return read_index != producer.index.load(std::memory_order::acquire);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,8 +244,7 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
|||||||
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
|
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
|
||||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
|
|
||||||
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
||||||
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
||||||
, invariant{invariant_}
|
, invariant{invariant_}
|
||||||
@@ -292,7 +291,7 @@ bool WallClock::IsNative() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||||
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
|
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
|
||||||
}
|
}
|
||||||
#elif defined(HAS_NCE)
|
#elif defined(HAS_NCE)
|
||||||
namespace {
|
namespace {
|
||||||
@@ -417,7 +416,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
|||||||
const WallClock g_wall_clock = [] {
|
const WallClock g_wall_clock = [] {
|
||||||
#if defined(ARCHITECTURE_x86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
auto const& caps = Common::g_cpu_caps;
|
auto const& caps = Common::g_cpu_caps;
|
||||||
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
|
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
|
||||||
#elif defined(HAS_NCE)
|
#elif defined(HAS_NCE)
|
||||||
return WallClock(false, 1);
|
return WallClock(false, 1);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ public:
|
|||||||
u64 ns_rdtsc_factor;
|
u64 ns_rdtsc_factor;
|
||||||
u64 us_rdtsc_factor;
|
u64 us_rdtsc_factor;
|
||||||
u64 ms_rdtsc_factor;
|
u64 ms_rdtsc_factor;
|
||||||
u64 rdtsc_ns_integer;
|
|
||||||
u64 rdtsc_ns_factor;
|
u64 rdtsc_ns_factor;
|
||||||
u64 cntpct_rdtsc_factor;
|
u64 cntpct_rdtsc_factor;
|
||||||
u64 gputick_rdtsc_factor;
|
u64 gputick_rdtsc_factor;
|
||||||
|
|||||||
+20
-18
@@ -17,33 +17,35 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
|
|
||||||
// We don't need to use the preprocessor, we can just select depending on return type
|
|
||||||
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
|
|
||||||
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
|
|
||||||
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
|
|
||||||
template<> std::string HandleStrerrorR(int r, char *err_str) {
|
|
||||||
return std::string{r != 0
|
|
||||||
? "(strerror_r failed to format error)"
|
|
||||||
: err_str};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string NativeErrorToString(int e) {
|
std::string NativeErrorToString(int e) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LPSTR err_str;
|
LPSTR err_str;
|
||||||
|
|
||||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
LPSTR(&err_str), 1, nullptr);
|
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||||
if (res) {
|
if (!res) {
|
||||||
std::string ret(err_str);
|
return "(FormatMessageA failed to format error)";
|
||||||
LocalFree(err_str);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
return "(FormatMessageA failed to format error)";
|
std::string ret(err_str);
|
||||||
|
LocalFree(err_str);
|
||||||
|
return ret;
|
||||||
#else
|
#else
|
||||||
char err_str[255];
|
char err_str[255];
|
||||||
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
#if defined(__ANDROID__) || \
|
||||||
|
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
|
||||||
|
// Thread safe (GNU-specific)
|
||||||
|
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||||
|
return std::string(str);
|
||||||
|
#else
|
||||||
|
// Thread safe (XSI-compliant)
|
||||||
|
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||||
|
if (second_err != 0) {
|
||||||
|
return "(strerror_r failed to format error)";
|
||||||
|
}
|
||||||
|
return std::string(err_str);
|
||||||
|
#endif // GLIBC etc.
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-65
@@ -5,7 +5,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "common/container/unordered_map.h"
|
#include "common/container/unordered_map.h"
|
||||||
@@ -481,80 +480,28 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
|||||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||||
path.end());
|
path.end());
|
||||||
|
|
||||||
std::string root;
|
const bool absolute = !path.empty() && path[0] == type2;
|
||||||
std::string_view components{path};
|
|
||||||
bool drive_relative = false;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
|
|
||||||
const bool drive =
|
|
||||||
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
|
|
||||||
|
|
||||||
if (network) {
|
|
||||||
root.assign(2, type2);
|
|
||||||
components.remove_prefix(2);
|
|
||||||
} else if (drive) {
|
|
||||||
root.assign(path.data(), 2);
|
|
||||||
components.remove_prefix(2);
|
|
||||||
if (!components.empty() && components.front() == type2) {
|
|
||||||
root += type2;
|
|
||||||
components.remove_prefix(1);
|
|
||||||
} else {
|
|
||||||
drive_relative = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (root.empty() && !components.empty() && components.front() == type2) {
|
|
||||||
root += type2;
|
|
||||||
components.remove_prefix(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto path_parts = SplitPathComponents(components);
|
|
||||||
std::size_t root_component_count = 0;
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (network) {
|
|
||||||
root_component_count = 2;
|
|
||||||
|
|
||||||
const auto is_unc = [](std::string_view part) {
|
|
||||||
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
|
|
||||||
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
|
|
||||||
};
|
|
||||||
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
|
|
||||||
root_component_count = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<std::string_view> parts;
|
std::vector<std::string_view> parts;
|
||||||
for (std::size_t i = 0; i < path_parts.size(); ++i) {
|
|
||||||
const auto part = path_parts[i];
|
for (const auto part : SplitPathComponents(path))
|
||||||
if (i < root_component_count) {
|
{
|
||||||
parts.push_back(part);
|
if (part.empty() || part == ".")
|
||||||
} else if (part.empty() || part == ".") {
|
|
||||||
continue;
|
continue;
|
||||||
} else if (part == "..") {
|
if (part == ".." && !parts.empty() && parts.back() != "..")
|
||||||
if (parts.size() > root_component_count) {
|
parts.pop_back();
|
||||||
parts.pop_back();
|
else if (part != "..") parts.push_back(part);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parts.push_back(part);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t root_length = root.size();
|
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
||||||
std::string resolved = std::move(root);
|
for (std::size_t i = 0; i < parts.size(); ++i)
|
||||||
for (std::size_t i = 0; i < parts.size(); ++i) {
|
{
|
||||||
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
|
if (i != 0)
|
||||||
resolved += type2;
|
resolved += type2;
|
||||||
resolved.append(parts[i].data(), parts[i].size());
|
resolved.append(parts[i].data(), parts[i].size());
|
||||||
}
|
}
|
||||||
|
|
||||||
path = std::move(resolved);
|
path = std::move(resolved);
|
||||||
|
|
||||||
if (!path.empty() && path.size() == root_length) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return std::string(RemoveTrailingSlash(path));
|
return std::string(RemoveTrailingSlash(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,9 +347,8 @@ enum class DirectorySeparator {
|
|||||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||||
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
||||||
|
|
||||||
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
|
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||||
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
|
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||||
// preserved.
|
|
||||||
[[nodiscard]] std::string SanitizePath(
|
[[nodiscard]] std::string SanitizePath(
|
||||||
std::string_view path,
|
std::string_view path,
|
||||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||||
|
|||||||
+22
-28
@@ -4,34 +4,17 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include "common/heap_tracker.h"
|
#include "common/heap_tracker.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/memory_detect.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
s64 GetMaxPermissibleResidentMapCount() {
|
|
||||||
// Default value.
|
|
||||||
s64 value = 65530;
|
|
||||||
|
|
||||||
// Try to read how many mappings we can make.
|
|
||||||
std::ifstream s("/proc/sys/vm/max_map_count");
|
|
||||||
s >> value;
|
|
||||||
|
|
||||||
// Print, for debug.
|
|
||||||
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
|
|
||||||
|
|
||||||
// Allow 20000 maps for other code and to account for split inaccuracy.
|
|
||||||
return std::max<s64>(value - 20000, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
||||||
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
|
: m_buffer(buffer),
|
||||||
|
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
|
||||||
|
m_max_resident_map_count(static_cast<s64>(GetPermissibleMapCount())) {}
|
||||||
HeapTracker::~HeapTracker() = default;
|
HeapTracker::~HeapTracker() = default;
|
||||||
|
|
||||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||||
@@ -85,7 +68,8 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
|
|||||||
|
|
||||||
// If resident, erase from resident map.
|
// If resident, erase from resident map.
|
||||||
if (item->is_resident) {
|
if (item->is_resident) {
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
|
||||||
|
ASSERT(m_resident_map_count >= 0);
|
||||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +175,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
|||||||
|
|
||||||
// This map is now resident.
|
// This map is now resident.
|
||||||
it->is_resident = true;
|
it->is_resident = true;
|
||||||
m_resident_map_count++;
|
m_resident_map_count += this->HostMapCount(it->paddr, it->size);
|
||||||
m_resident_mappings.insert(*it);
|
m_resident_mappings.insert(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,17 +197,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
|||||||
// Despite being worse in theory, this has proven to be better in practice than more
|
// Despite being worse in theory, this has proven to be better in practice than more
|
||||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||||
// lock contention.
|
// lock contention.
|
||||||
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||||
std::size_t const evict_count = m_resident_map_count - desired_count;
|
|
||||||
auto it = m_resident_mappings.begin();
|
auto it = m_resident_mappings.begin();
|
||||||
|
|
||||||
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
|
||||||
// Unmark and unmap.
|
// Unmark and unmap.
|
||||||
it->is_resident = false;
|
it->is_resident = false;
|
||||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
ASSERT(--m_resident_map_count >= 0);
|
m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
|
||||||
|
ASSERT(m_resident_map_count >= 0);
|
||||||
it = m_resident_mappings.erase(it);
|
it = m_resident_mappings.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,6 +229,7 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
// Cache the original values.
|
// Cache the original values.
|
||||||
auto* const left = std::addressof(*it);
|
auto* const left = std::addressof(*it);
|
||||||
const size_t orig_size = left->size;
|
const size_t orig_size = left->size;
|
||||||
|
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
|
||||||
|
|
||||||
// Adjust the left map.
|
// Adjust the left map.
|
||||||
const size_t left_size = offset - left->vaddr;
|
const size_t left_size = offset - left->vaddr;
|
||||||
@@ -266,11 +251,20 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
|
|
||||||
// If resident, also insert into resident map.
|
// If resident, also insert into resident map.
|
||||||
if (right->is_resident) {
|
if (right->is_resident) {
|
||||||
m_resident_map_count++;
|
m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
|
||||||
|
this->HostMapCount(right->paddr, right->size) -
|
||||||
|
orig_host_map_count;
|
||||||
m_resident_mappings.insert(*right);
|
m_resident_mappings.insert(*right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
|
||||||
|
if (!m_has_hardware_buffer_backing) {
|
||||||
|
return size != 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
|
||||||
|
}
|
||||||
|
|
||||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||||
const SeparateHeapMap key{
|
const SeparateHeapMap key{
|
||||||
.vaddr = offset,
|
.vaddr = offset,
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -82,10 +85,13 @@ private:
|
|||||||
|
|
||||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
|
s64 HostMapCount(PAddr paddr, size_t size) const;
|
||||||
|
|
||||||
void RebuildSeparateHeapAddressSpace();
|
void RebuildSeparateHeapAddressSpace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::HostMemory& m_buffer;
|
Common::HostMemory& m_buffer;
|
||||||
|
const bool m_has_hardware_buffer_backing;
|
||||||
const s64 m_max_resident_map_count;
|
const s64 m_max_resident_map_count;
|
||||||
|
|
||||||
std::shared_mutex m_rebuild_lock{};
|
std::shared_mutex m_rebuild_lock{};
|
||||||
|
|||||||
+393
-6
@@ -51,14 +51,65 @@
|
|||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/free_region_manager.h"
|
#include "common/free_region_manager.h"
|
||||||
#include "common/host_memory.h"
|
#include "common/host_memory.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "common/memory_detect.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <cerrno>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <android/hardware_buffer.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct NativeHandle {
|
||||||
|
int version;
|
||||||
|
int numFds;
|
||||||
|
int numInts;
|
||||||
|
int data[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
|
||||||
|
|
||||||
|
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
|
||||||
|
void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
|
||||||
|
if (lib == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
|
||||||
|
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DmaBufSync {
|
||||||
|
u64 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u64 DmaBufSyncRead = 1ULL << 0;
|
||||||
|
constexpr u64 DmaBufSyncWrite = 1ULL << 1;
|
||||||
|
constexpr u64 DmaBufSyncStart = 0ULL << 2;
|
||||||
|
constexpr u64 DmaBufSyncEnd = 1ULL << 2;
|
||||||
|
|
||||||
|
void SyncDmaBufCpuAccess(int fd, u64 phase) {
|
||||||
|
DmaBufSync sync{.flags = phase | DmaBufSyncRead | DmaBufSyncWrite};
|
||||||
|
while (ioctl(fd, _IOW('b', 0, DmaBufSync), &sync) != 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
@@ -75,6 +126,12 @@ namespace Common {
|
|||||||
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
||||||
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
||||||
|
|
||||||
|
static std::atomic<u64> committed_backing_size{};
|
||||||
|
|
||||||
|
u64 GetCommittedBackingSize() noexcept {
|
||||||
|
return committed_backing_size.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
// Manually imported for MinGW compatibility
|
// Manually imported for MinGW compatibility
|
||||||
@@ -122,7 +179,7 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
, process{GetCurrentProcess()}
|
, process{GetCurrentProcess()}
|
||||||
@@ -236,6 +293,10 @@ public:
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBackingShared() const noexcept {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
|
|
||||||
@@ -512,9 +573,10 @@ static int shm_open_anon(int flags, mode_t mode) {
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
|
, preferred_offset{preferred_offset_}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool Init() {
|
bool Init() {
|
||||||
@@ -554,10 +616,15 @@ public:
|
|||||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
fd = -1;
|
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
fd = -1;
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
if (InitAhbBacking()) {
|
||||||
|
return InitVirtual();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||||
}
|
}
|
||||||
if (backing_base == MAP_FAILED) {
|
if (backing_base == MAP_FAILED) {
|
||||||
@@ -565,7 +632,10 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Virtual memory initialization
|
return InitVirtual();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InitVirtual() {
|
||||||
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
||||||
if (virtual_base == MAP_FAILED) {
|
if (virtual_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||||
@@ -578,6 +648,244 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
|
||||||
|
return AHardwareBuffer_Desc{
|
||||||
|
.width = static_cast<u32>(len),
|
||||||
|
.height = 1,
|
||||||
|
.layers = 1,
|
||||||
|
.format = AHARDWAREBUFFER_FORMAT_BLOB,
|
||||||
|
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
||||||
|
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
|
||||||
|
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
|
||||||
|
.stride = 0,
|
||||||
|
.rfu0 = 0,
|
||||||
|
.rfu1 = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
|
||||||
|
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
|
||||||
|
AHardwareBuffer* buffer{};
|
||||||
|
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const NativeHandle* const handle = get_native_handle(buffer);
|
||||||
|
if (handle == nullptr || handle->numFds < 1) {
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const int probe_fd = handle->data[0];
|
||||||
|
bool ok = true;
|
||||||
|
const auto try_map = [&](int prot, off_t offset) {
|
||||||
|
if (!ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
|
||||||
|
if (ptr == MAP_FAILED) {
|
||||||
|
ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
munmap(ptr, PageAlignment);
|
||||||
|
};
|
||||||
|
try_map(PROT_READ | PROT_WRITE, 0);
|
||||||
|
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment));
|
||||||
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
try_map(PROT_READ | PROT_EXEC, 0);
|
||||||
|
#endif
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ComputeAhbBudget(size_t window_size) const {
|
||||||
|
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
|
||||||
|
constexpr u64 BaselineFootprint = 6ULL << 30;
|
||||||
|
if (total_physical <= BaselineFootprint) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const u64 permissible_maps = Common::GetPermissibleMapCount();
|
||||||
|
if (permissible_maps == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u64 budget = (total_physical - BaselineFootprint) / 2;
|
||||||
|
constexpr u64 MapSlotsPerWindow = 64;
|
||||||
|
const u64 affordable_windows = permissible_maps / MapSlotsPerWindow;
|
||||||
|
budget = (std::min)(budget, affordable_windows * window_size);
|
||||||
|
const u64 available = Common::GetAvailablePhysicalMemory();
|
||||||
|
if (available != 0) {
|
||||||
|
budget = (std::min)(budget, available / 2);
|
||||||
|
}
|
||||||
|
budget = (std::min)(budget, static_cast<u64>(backing_size));
|
||||||
|
budget = Common::AlignDown(budget, window_size);
|
||||||
|
constexpr u64 MinimumBudget = 256ULL << 20;
|
||||||
|
if (budget < MinimumBudget) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<size_t>(budget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InitAhbBacking() {
|
||||||
|
if (!Settings::values.use_unified_memory.GetValue()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
|
||||||
|
ResolveGetNativeHandle();
|
||||||
|
if (get_native_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
constexpr size_t window_size = 256ULL << 20;
|
||||||
|
const size_t budget = ComputeAhbBudget(window_size);
|
||||||
|
if (budget == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ProbeAhbBacking(get_native_handle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
|
||||||
|
const size_t max_windows = (std::min)(budget, aligned_backing) / window_size;
|
||||||
|
|
||||||
|
std::vector<AHardwareBuffer*> buffers;
|
||||||
|
std::vector<int> buffer_fds;
|
||||||
|
const auto cleanup = [&] {
|
||||||
|
for (AHardwareBuffer* buffer : buffers) {
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
}
|
||||||
|
buffers.clear();
|
||||||
|
buffer_fds.clear();
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < max_windows; ++i) {
|
||||||
|
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
|
||||||
|
AHardwareBuffer* buffer{};
|
||||||
|
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const NativeHandle* const handle = get_native_handle(buffer);
|
||||||
|
if (handle == nullptr || handle->numFds < 1) {
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const int buffer_fd = handle->data[0];
|
||||||
|
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
|
||||||
|
if (buffer_len < static_cast<off_t>(window_size)) {
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffers.push_back(buffer);
|
||||||
|
buffer_fds.push_back(buffer_fd);
|
||||||
|
}
|
||||||
|
const size_t num_windows = buffers.size();
|
||||||
|
if (num_windows == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t region_size = num_windows * window_size;
|
||||||
|
const size_t region_base = Common::AlignDown(
|
||||||
|
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
|
||||||
|
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
|
||||||
|
if (base == MAP_FAILED) {
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto map_over_reservation = [&](size_t offset, size_t len, int map_fd,
|
||||||
|
off_t map_offset) {
|
||||||
|
if (len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
|
||||||
|
map_offset) == MAP_FAILED) {
|
||||||
|
munmap(base, backing_size);
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (!map_over_reservation(0, region_base, fd, 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < num_windows; ++i) {
|
||||||
|
if (!map_over_reservation(region_base + i * window_size, window_size, buffer_fds[i],
|
||||||
|
0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const size_t tail_offset = region_base + region_size;
|
||||||
|
if (!map_over_reservation(tail_offset, backing_size - tail_offset, fd,
|
||||||
|
static_cast<off_t>(tail_offset))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
backing_base = base;
|
||||||
|
ahb_windows = std::move(buffers);
|
||||||
|
ahb_fds = std::move(buffer_fds);
|
||||||
|
ahb_window_size = window_size;
|
||||||
|
ahb_base = region_base;
|
||||||
|
ahb_bytes = region_size;
|
||||||
|
committed_backing_size.store(region_size, std::memory_order_relaxed);
|
||||||
|
for (const int window_fd : ahb_fds) {
|
||||||
|
SyncDmaBufCpuAccess(window_fd, DmaBufSyncStart);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapBackingRange(size_t virtual_offset, size_t host_offset, size_t length, int prot_flags) {
|
||||||
|
while (length > 0) {
|
||||||
|
int map_fd = fd;
|
||||||
|
off_t map_offset = static_cast<off_t>(host_offset);
|
||||||
|
size_t chunk = length;
|
||||||
|
if (host_offset < ahb_base) {
|
||||||
|
chunk = (std::min)(chunk, ahb_base - host_offset);
|
||||||
|
} else if (host_offset < ahb_base + ahb_bytes) {
|
||||||
|
const size_t relative = host_offset - ahb_base;
|
||||||
|
const size_t window = relative / ahb_window_size;
|
||||||
|
const size_t local = relative % ahb_window_size;
|
||||||
|
map_fd = ahb_fds[window];
|
||||||
|
map_offset = static_cast<off_t>(local);
|
||||||
|
chunk = (std::min)(chunk, ahb_window_size - local);
|
||||||
|
}
|
||||||
|
void* const ret = mmap(virtual_base + virtual_offset, chunk, prot_flags,
|
||||||
|
MAP_SHARED | MAP_FIXED, map_fd, map_offset);
|
||||||
|
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||||
|
virtual_offset += chunk;
|
||||||
|
host_offset += chunk;
|
||||||
|
length -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
||||||
|
if (length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ahb_bytes == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t count = 0;
|
||||||
|
while (length > 0) {
|
||||||
|
size_t chunk = length;
|
||||||
|
if (host_offset < ahb_base) {
|
||||||
|
chunk = (std::min)(chunk, ahb_base - host_offset);
|
||||||
|
} else if (host_offset < ahb_base + ahb_bytes) {
|
||||||
|
const size_t local = (host_offset - ahb_base) % ahb_window_size;
|
||||||
|
chunk = (std::min)(chunk, ahb_window_size - local);
|
||||||
|
}
|
||||||
|
host_offset += chunk;
|
||||||
|
length -= chunk;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
|
||||||
|
return ahb_windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AhbWindowSize() const noexcept {
|
||||||
|
return ahb_bytes != 0 ? ahb_window_size : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AhbBase() const noexcept {
|
||||||
|
return ahb_base;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
@@ -606,6 +914,12 @@ public:
|
|||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
if (True(perms & MemoryPermission::Execute))
|
if (True(perms & MemoryPermission::Execute))
|
||||||
prot_flags |= PROT_EXEC;
|
prot_flags |= PROT_EXEC;
|
||||||
|
#endif
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
if (ahb_bytes != 0) {
|
||||||
|
MapBackingRange(virtual_offset, host_offset, length, prot_flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||||
@@ -651,8 +965,18 @@ public:
|
|||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBackingShared() const noexcept {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
if (ahb_bytes != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return fd >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
|
const size_t preferred_offset;
|
||||||
|
|
||||||
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
@@ -675,6 +999,21 @@ private:
|
|||||||
int ret = close(fd);
|
int ret = close(fd);
|
||||||
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
for (const int window_fd : ahb_fds) {
|
||||||
|
SyncDmaBufCpuAccess(window_fd, DmaBufSyncEnd);
|
||||||
|
}
|
||||||
|
for (AHardwareBuffer* buffer : ahb_windows) {
|
||||||
|
AHardwareBuffer_release(buffer);
|
||||||
|
}
|
||||||
|
ahb_windows.clear();
|
||||||
|
ahb_fds.clear();
|
||||||
|
if (ahb_bytes != 0) {
|
||||||
|
committed_backing_size.store(0, std::memory_order_relaxed);
|
||||||
|
ahb_bytes = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
||||||
@@ -700,11 +1039,19 @@ private:
|
|||||||
|
|
||||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||||
FreeRegionManager free_manager{};
|
FreeRegionManager free_manager{};
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
std::vector<AHardwareBuffer*> ahb_windows;
|
||||||
|
std::vector<int> ahb_fds;
|
||||||
|
size_t ahb_window_size{};
|
||||||
|
size_t ahb_base{};
|
||||||
|
size_t ahb_bytes{};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
||||||
: backing_size(backing_size_)
|
: backing_size(backing_size_)
|
||||||
, virtual_size(virtual_size_)
|
, virtual_size(virtual_size_)
|
||||||
{
|
{
|
||||||
@@ -714,7 +1061,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
|||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
#else
|
#else
|
||||||
// Try to allocate a fastmem arena.
|
// Try to allocate a fastmem arena.
|
||||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize, preferred_offset_);
|
||||||
if (impl->Init()) {
|
if (impl->Init()) {
|
||||||
backing_base = impl->backing_base;
|
backing_base = impl->backing_base;
|
||||||
virtual_base = impl->virtual_base;
|
virtual_base = impl->virtual_base;
|
||||||
@@ -794,6 +1141,46 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
|
|||||||
std::memset(backing_base + physical_offset, fill_value, length);
|
std::memset(backing_base + physical_offset, fill_value, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
|
||||||
|
#else
|
||||||
|
return {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
|
||||||
|
#else
|
||||||
|
return length != 0 ? 1 : 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
return impl ? impl->AhbWindowSize() : 0;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostMemory::IsBackingShared() const noexcept {
|
||||||
|
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return impl && impl->IsBackingShared();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HostMemory::BackingHardwareBufferBase() const noexcept {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
return impl ? impl->AhbBase() : 0;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void HostMemory::EnableDirectMappedAddress() {
|
void HostMemory::EnableDirectMappedAddress() {
|
||||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||||
if (impl) {
|
if (impl) {
|
||||||
|
|||||||
@@ -8,11 +8,16 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
struct AHardwareBuffer;
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
|
||||||
|
|
||||||
enum class MemoryPermission : u32 {
|
enum class MemoryPermission : u32 {
|
||||||
Read = 1 << 0,
|
Read = 1 << 0,
|
||||||
Write = 1 << 1,
|
Write = 1 << 1,
|
||||||
@@ -27,7 +32,7 @@ DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
|
|||||||
*/
|
*/
|
||||||
class HostMemory {
|
class HostMemory {
|
||||||
public:
|
public:
|
||||||
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
|
explicit HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_ = 0);
|
||||||
~HostMemory();
|
~HostMemory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +66,20 @@ public:
|
|||||||
return backing_base;
|
return backing_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t BackingSize() const noexcept {
|
||||||
|
return backing_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] size_t BackingHardwareBufferBase() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsBackingShared() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -17,6 +20,10 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "common/memory_detect.h"
|
#include "common/memory_detect.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@@ -69,4 +76,61 @@ const MemoryInfo& GetMemInfo() {
|
|||||||
return mem_info;
|
return mem_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 GetPermissibleMapCount() {
|
||||||
|
constexpr u64 DefaultMapCount = 65530;
|
||||||
|
constexpr u64 ReservedMaps = 20000;
|
||||||
|
u64 count = DefaultMapCount;
|
||||||
|
#ifdef __linux__
|
||||||
|
if (std::FILE* const file = std::fopen("/proc/sys/vm/max_map_count", "re")) {
|
||||||
|
char line[32];
|
||||||
|
if (std::fgets(line, sizeof(line), file) != nullptr) {
|
||||||
|
const u64 parsed = std::strtoull(line, nullptr, 10);
|
||||||
|
if (parsed != 0) {
|
||||||
|
count = parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (count <= ReservedMaps) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return count - ReservedMaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetAvailablePhysicalMemory() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
MEMORYSTATUSEX memorystatus;
|
||||||
|
memorystatus.dwLength = sizeof(memorystatus);
|
||||||
|
if (GlobalMemoryStatusEx(&memorystatus) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return memorystatus.ullAvailPhys;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
static constexpr char AvailableKey[] = "MemAvailable:";
|
||||||
|
if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) {
|
||||||
|
char line[256];
|
||||||
|
u64 available = 0;
|
||||||
|
while (std::fgets(line, sizeof(line), file) != nullptr) {
|
||||||
|
if (std::strncmp(line, AvailableKey, sizeof(AvailableKey) - 1) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
available = std::strtoull(line + sizeof(AvailableKey) - 1, nullptr, 10) * 1024;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::fclose(file);
|
||||||
|
if (available != 0) {
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct sysinfo meminfo;
|
||||||
|
if (sysinfo(&meminfo) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<u64>(meminfo.freeram) * static_cast<u64>(meminfo.mem_unit);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -18,4 +21,8 @@ struct MemoryInfo {
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
||||||
|
|
||||||
|
[[nodiscard]] u64 GetPermissibleMapCount();
|
||||||
|
|
||||||
|
[[nodiscard]] u64 GetAvailablePhysicalMemory();
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -96,15 +96,15 @@ struct PageTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write page info atomically
|
/// Write page info atomically
|
||||||
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MarkRasterizerCached() noexcept {
|
constexpr void MarkRasterizerCached() noexcept {
|
||||||
data_raw.fetch_or(0b111);
|
data_raw.fetch_or(0b111);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MarkDebug(u64 ptr, u16 block) noexcept {
|
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||||
Store(true, PageType::DebugMemory, block, ptr);
|
Store(true, PageType::DebugMemory, block, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -666,6 +666,9 @@ struct Values {
|
|||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
|
||||||
|
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
|
||||||
|
Category::RendererHacks};
|
||||||
|
|
||||||
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
||||||
GpuUnswizzleSize::Large,
|
GpuUnswizzleSize::Large,
|
||||||
"gpu_unswizzle_texture_size",
|
"gpu_unswizzle_texture_size",
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
|||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
// MSVC doesn't regard structs with atomics as trivially copyable
|
requires std::is_trivially_copyable_v<T>
|
||||||
// requires std::is_trivially_copyable_v<T>
|
|
||||||
class SparseLargeVector final {
|
class SparseLargeVector final {
|
||||||
public:
|
public:
|
||||||
constexpr SparseLargeVector() = default;
|
constexpr SparseLargeVector() = default;
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 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
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#elif defined(ARCHITECTURE_x86_64)
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
|
||||||
|
/// supervisor. Should be used on short sequences of code.
|
||||||
|
struct SpinLock {
|
||||||
|
SpinLock() noexcept = default;
|
||||||
|
SpinLock(const SpinLock&) noexcept = delete;
|
||||||
|
SpinLock& operator=(const SpinLock&) noexcept = delete;
|
||||||
|
SpinLock(SpinLock&&) noexcept = delete;
|
||||||
|
SpinLock& operator=(SpinLock&&) noexcept = delete;
|
||||||
|
|
||||||
|
inline void lock() noexcept {
|
||||||
|
while (lck.test_and_set(std::memory_order_acquire)) {
|
||||||
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
|
_mm_pause();
|
||||||
|
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
|
||||||
|
__yield();
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
asm("yield");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unlock() noexcept {
|
||||||
|
lck.clear(std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool try_lock() noexcept {
|
||||||
|
return !lck.test_and_set(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
+2
-2
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
|
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
||||||
UUID uuid{};
|
UUID uuid{};
|
||||||
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
|
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
|
||||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||||
return uuid;
|
return uuid;
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ struct UUID {
|
|||||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
||||||
|
|
||||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
|
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
|
||||||
|
|
||||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -796,8 +796,6 @@ add_library(core STATIC
|
|||||||
hle/service/ns/application_manager_interface.h
|
hle/service/ns/application_manager_interface.h
|
||||||
hle/service/ns/application_version_interface.cpp
|
hle/service/ns/application_version_interface.cpp
|
||||||
hle/service/ns/application_version_interface.h
|
hle/service/ns/application_version_interface.h
|
||||||
hle/service/ns/async_result.cpp
|
|
||||||
hle/service/ns/async_result.h
|
|
||||||
hle/service/ns/content_management_interface.cpp
|
hle/service/ns/content_management_interface.cpp
|
||||||
hle/service/ns/content_management_interface.h
|
hle/service/ns/content_management_interface.h
|
||||||
hle/service/ns/develop_interface.cpp
|
hle/service/ns/develop_interface.cpp
|
||||||
|
|||||||
@@ -22,15 +22,21 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
|
|||||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
||||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||||
switch (size) {
|
return m_memory.Read8(vaddr);
|
||||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
}
|
||||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
||||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
return m_memory.Read16(vaddr);
|
||||||
default: UNREACHABLE();
|
}
|
||||||
}
|
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
|
||||||
|
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||||
|
return m_memory.Read32(vaddr);
|
||||||
|
}
|
||||||
|
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
|
||||||
|
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||||
|
return m_memory.Read64(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||||
@@ -44,17 +50,27 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
|||||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
||||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||||
switch (size) {
|
m_memory.Write8(vaddr, value);
|
||||||
case sizeof(u64): return m_memory.Write64(vaddr, value);
|
|
||||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
|
||||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
|
||||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
|
||||||
default: UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
|
||||||
|
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
m_memory.Write16(vaddr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
|
||||||
|
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
m_memory.Write32(vaddr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
|
||||||
|
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
m_memory.Write64(vaddr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
||||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||||
m_memory.WriteExclusive8(vaddr, value, expected);
|
m_memory.WriteExclusive8(vaddr, value, expected);
|
||||||
@@ -161,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
config.page_table_marked_bit = uint8_t(0);
|
config.page_table_marked_bit = 0;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||||
@@ -177,7 +193,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||||
// we have to manually sign extend when our actual pointer is negative.
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,18 @@ class System;
|
|||||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
||||||
u64 MemoryRead(u32 vaddr, size_t size) override;
|
u8 MemoryRead8(u32 vaddr) override;
|
||||||
|
u16 MemoryRead16(u32 vaddr) override;
|
||||||
|
u32 MemoryRead32(u32 vaddr) override;
|
||||||
|
u64 MemoryRead64(u32 vaddr) override;
|
||||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
|
void MemoryWrite8(u32 vaddr, u8 value) override;
|
||||||
|
void MemoryWrite16(u32 vaddr, u16 value) override;
|
||||||
|
void MemoryWrite32(u32 vaddr, u32 value) override;
|
||||||
|
void MemoryWrite64(u32 vaddr, u64 value) override;
|
||||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
||||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
||||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@@ -22,15 +21,21 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
|
|||||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
||||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||||
switch (size) {
|
return m_memory.Read8(vaddr);
|
||||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
}
|
||||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
||||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
return m_memory.Read16(vaddr);
|
||||||
default: UNREACHABLE();
|
}
|
||||||
}
|
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
|
||||||
|
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||||
|
return m_memory.Read32(vaddr);
|
||||||
|
}
|
||||||
|
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
|
||||||
|
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||||
|
return m_memory.Read64(vaddr);
|
||||||
}
|
}
|
||||||
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||||
@@ -48,15 +53,24 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
|||||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
||||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||||
switch (size) {
|
m_memory.Write8(vaddr, value);
|
||||||
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
|
}
|
||||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
}
|
||||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
||||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||||
default: UNREACHABLE();
|
m_memory.Write16(vaddr, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
|
||||||
|
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
m_memory.Write32(vaddr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
|
||||||
|
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||||
|
m_memory.Write64(vaddr, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
||||||
@@ -202,7 +216,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
config.page_table_marked_bit = uint8_t(0);
|
config.page_table_marked_bit = 0;
|
||||||
config.silently_mirror_page_table = false;
|
config.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
@@ -221,7 +235,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||||
// we have to manually sign extend when our actual pointer is negative.
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
@@ -37,13 +36,19 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
|||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||||
|
|
||||||
u64 MemoryRead(u64 vaddr, size_t size) override;
|
u8 MemoryRead8(u64 vaddr) override;
|
||||||
|
u16 MemoryRead16(u64 vaddr) override;
|
||||||
|
u32 MemoryRead32(u64 vaddr) override;
|
||||||
|
u64 MemoryRead64(u64 vaddr) override;
|
||||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
|
void MemoryWrite8(u64 vaddr, u8 value) override;
|
||||||
|
void MemoryWrite16(u64 vaddr, u16 value) override;
|
||||||
|
void MemoryWrite32(u64 vaddr, u32 value) override;
|
||||||
|
void MemoryWrite64(u64 vaddr, u64 value) override;
|
||||||
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
||||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
|
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
|
||||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
|
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
|
||||||
|
|||||||
+28
-46
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
|||||||
|
|
||||||
struct System::Impl {
|
struct System::Impl {
|
||||||
explicit Impl(System& system)
|
explicit Impl(System& system)
|
||||||
: kernel{system}
|
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
|
||||||
, fs_controller{system}
|
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
|
||||||
, hid_core{kernel}
|
|
||||||
, cpu_manager{system}
|
|
||||||
, reporter{system}
|
|
||||||
, profile_manager{}
|
|
||||||
{}
|
|
||||||
|
|
||||||
u64 program_id;
|
u64 program_id;
|
||||||
|
|
||||||
@@ -125,16 +120,11 @@ struct System::Impl {
|
|||||||
|
|
||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
|
unified_memory = Settings::values.use_unified_memory.GetValue();
|
||||||
|
|
||||||
core_timing.SetMulticore(is_multicore);
|
core_timing.SetMulticore(is_multicore);
|
||||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||||
|
|
||||||
applet_manager.emplace(system);
|
|
||||||
frontend_applets.emplace(system);
|
|
||||||
apm_controller.emplace(core_timing);
|
|
||||||
arp_manager.emplace();
|
|
||||||
profile_manager.emplace();
|
|
||||||
|
|
||||||
// Create a default fs if one doesn't already exist.
|
// Create a default fs if one doesn't already exist.
|
||||||
if (virtual_filesystem == nullptr) {
|
if (virtual_filesystem == nullptr) {
|
||||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
@@ -144,7 +134,7 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create default implementations of applets if one is not provided.
|
// Create default implementations of applets if one is not provided.
|
||||||
frontend_applets->SetDefaultAppletsIfMissing();
|
frontend_applets.SetDefaultAppletsIfMissing();
|
||||||
|
|
||||||
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||||
|
|
||||||
@@ -158,7 +148,8 @@ struct System::Impl {
|
|||||||
!device_memory.has_value() ||
|
!device_memory.has_value() ||
|
||||||
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
||||||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
||||||
Settings::MemoryLayout::Memory_4Gb);
|
Settings::MemoryLayout::Memory_4Gb) ||
|
||||||
|
unified_memory != Settings::values.use_unified_memory.GetValue();
|
||||||
|
|
||||||
if (!must_reinitialize) {
|
if (!must_reinitialize) {
|
||||||
return;
|
return;
|
||||||
@@ -169,6 +160,7 @@ struct System::Impl {
|
|||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout =
|
extended_memory_layout =
|
||||||
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
|
unified_memory = Settings::values.use_unified_memory.GetValue();
|
||||||
|
|
||||||
Initialize(system);
|
Initialize(system);
|
||||||
}
|
}
|
||||||
@@ -387,7 +379,7 @@ struct System::Impl {
|
|||||||
|
|
||||||
// Register with applet manager
|
// Register with applet manager
|
||||||
// All threads are started, begin main process execution, now that we're in the clear
|
// All threads are started, begin main process execution, now that we're in the clear
|
||||||
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||||
|
|
||||||
if (Settings::values.gamecard_inserted) {
|
if (Settings::values.gamecard_inserted) {
|
||||||
if (Settings::values.gamecard_current_game) {
|
if (Settings::values.gamecard_current_game) {
|
||||||
@@ -439,27 +431,13 @@ struct System::Impl {
|
|||||||
core_timing.SyncPause(false);
|
core_timing.SyncPause(false);
|
||||||
Network::CancelPendingSocketOperations();
|
Network::CancelPendingSocketOperations();
|
||||||
kernel.SuspendEmulation(true);
|
kernel.SuspendEmulation(true);
|
||||||
kernel.ShutdownCores();
|
|
||||||
|
|
||||||
// Notify services helpers of shutdown
|
|
||||||
audio_core->Shutdown();
|
|
||||||
|
|
||||||
// Wait for threads/services to join
|
|
||||||
kernel.CloseServices();
|
kernel.CloseServices();
|
||||||
|
kernel.ShutdownCores();
|
||||||
// service shutdown
|
|
||||||
services.reset();
|
services.reset();
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
frontend_applets.reset();
|
|
||||||
applet_manager.reset();
|
|
||||||
apm_controller.reset();
|
|
||||||
arp_manager.reset();
|
|
||||||
profile_manager.reset();
|
|
||||||
|
|
||||||
fs_controller.Reset();
|
fs_controller.Reset();
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
core_timing.ClearPendingEvents();
|
core_timing.ClearPendingEvents();
|
||||||
core_timing.Reset();
|
|
||||||
app_loader.reset();
|
app_loader.reset();
|
||||||
audio_core.reset();
|
audio_core.reset();
|
||||||
gpu_core.reset();
|
gpu_core.reset();
|
||||||
@@ -476,6 +454,9 @@ struct System::Impl {
|
|||||||
room_member->SendGameInfo(game_info);
|
room_member->SendGameInfo(game_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset all glue registrations
|
||||||
|
arp_manager.ResetAll();
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Shutdown OK");
|
LOG_DEBUG(Core, "Shutdown OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,13 +485,13 @@ struct System::Impl {
|
|||||||
CpuManager cpu_manager;
|
CpuManager cpu_manager;
|
||||||
Reporter reporter;
|
Reporter reporter;
|
||||||
/// Applets
|
/// Applets
|
||||||
std::optional<Service::AM::AppletManager> applet_manager;
|
Service::AM::AppletManager applet_manager;
|
||||||
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets;
|
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
|
||||||
/// APM (Performance) services
|
/// APM (Performance) services
|
||||||
std::optional<Service::APM::Controller> apm_controller;
|
Service::APM::Controller apm_controller{core_timing};
|
||||||
/// Service State
|
/// Service State
|
||||||
std::optional<Service::Glue::ARPManager> arp_manager;
|
Service::Glue::ARPManager arp_manager;
|
||||||
std::optional<Service::Account::ProfileManager> profile_manager;
|
Service::Account::ProfileManager profile_manager;
|
||||||
/// Network instance
|
/// Network instance
|
||||||
Network::NetworkInstance network_instance;
|
Network::NetworkInstance network_instance;
|
||||||
Core::SpeedLimiter speed_limiter;
|
Core::SpeedLimiter speed_limiter;
|
||||||
@@ -557,6 +538,7 @@ struct System::Impl {
|
|||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
bool is_multicore : 1 = false;
|
bool is_multicore : 1 = false;
|
||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
|
bool unified_memory : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
|
|
||||||
@@ -837,19 +819,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
||||||
impl->frontend_applets->SetFrontendAppletSet(std::move(set));
|
impl->frontend_applets.SetFrontendAppletSet(std::move(set));
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
|
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
|
||||||
return *impl->frontend_applets;
|
return impl->frontend_applets;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
|
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
|
||||||
return *impl->frontend_applets;
|
return impl->frontend_applets;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::AM::AppletManager& System::GetAppletManager() {
|
Service::AM::AppletManager& System::GetAppletManager() {
|
||||||
return *impl->applet_manager;
|
return impl->applet_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
||||||
@@ -890,27 +872,27 @@ const Reporter& System::GetReporter() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Service::Glue::ARPManager& System::GetARPManager() {
|
Service::Glue::ARPManager& System::GetARPManager() {
|
||||||
return *impl->arp_manager;
|
return impl->arp_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Glue::ARPManager& System::GetARPManager() const {
|
const Service::Glue::ARPManager& System::GetARPManager() const {
|
||||||
return *impl->arp_manager;
|
return impl->arp_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::APM::Controller& System::GetAPMController() {
|
Service::APM::Controller& System::GetAPMController() {
|
||||||
return *impl->apm_controller;
|
return impl->apm_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::APM::Controller& System::GetAPMController() const {
|
const Service::APM::Controller& System::GetAPMController() const {
|
||||||
return *impl->apm_controller;
|
return impl->apm_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::Account::ProfileManager& System::GetProfileManager() {
|
Service::Account::ProfileManager& System::GetProfileManager() {
|
||||||
return *impl->profile_manager;
|
return impl->profile_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||||
return *impl->profile_manager;
|
return impl->profile_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetExitLocked(bool locked) {
|
void System::SetExitLocked(bool locked) {
|
||||||
|
|||||||
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
|
|||||||
|
|
||||||
void CoreTiming::Reset() {
|
void CoreTiming::Reset() {
|
||||||
paused = true;
|
paused = true;
|
||||||
|
pause_event.Set();
|
||||||
|
event.Set();
|
||||||
if (timer_thread.joinable()) {
|
if (timer_thread.joinable()) {
|
||||||
timer_thread.request_stop();
|
timer_thread.request_stop();
|
||||||
pause_event.Set();
|
|
||||||
event.Set();
|
|
||||||
timer_thread.join();
|
timer_thread.join();
|
||||||
}
|
}
|
||||||
has_started = false;
|
has_started = false;
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -12,9 +15,18 @@ constexpr size_t VirtualReserveSize = 1ULL << 38;
|
|||||||
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
size_t ApplicationPoolOffset() {
|
||||||
|
using Init = Kernel::Board::Nintendo::Nx::KSystemControl::Init;
|
||||||
|
const size_t dram_size = Init::GetIntendedMemorySize();
|
||||||
|
const size_t application_pool_size = Init::GetApplicationPoolSize();
|
||||||
|
return dram_size > application_pool_size ? dram_size - application_pool_size : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DeviceMemory::DeviceMemory()
|
DeviceMemory::DeviceMemory()
|
||||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||||
VirtualReserveSize} {}
|
VirtualReserveSize, ApplicationPoolOffset()} {}
|
||||||
|
|
||||||
DeviceMemory::~DeviceMemory() = default;
|
DeviceMemory::~DeviceMemory() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/sparse_large_vector.h"
|
#include "common/sparse_large_vector.h"
|
||||||
|
|
||||||
|
struct AHardwareBuffer;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
||||||
@@ -95,6 +97,34 @@ public:
|
|||||||
ApplyOpOnPAddr(address, buffer, operation);
|
ApplyOpOnPAddr(address, buffer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8* GetPhysicalBase() noexcept {
|
||||||
|
return reinterpret_cast<u8*>(physical_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8* GetPhysicalBase() const noexcept {
|
||||||
|
return reinterpret_cast<const u8*>(physical_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPhysicalSize() const noexcept {
|
||||||
|
return physical_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
|
||||||
|
return ahb_windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBackingHardwareBufferWindowSize() const noexcept {
|
||||||
|
return ahb_window_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBackingHardwareBufferBase() const noexcept {
|
||||||
|
return ahb_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBackingShared() const noexcept {
|
||||||
|
return backing_is_shared;
|
||||||
|
}
|
||||||
|
|
||||||
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
||||||
PAddr subbits = PAddr(address & page_mask);
|
PAddr subbits = PAddr(address & page_mask);
|
||||||
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
||||||
@@ -171,6 +201,11 @@ private:
|
|||||||
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
||||||
|
|
||||||
const uintptr_t physical_base;
|
const uintptr_t physical_base;
|
||||||
|
const size_t physical_size;
|
||||||
|
const std::span<AHardwareBuffer* const> ahb_windows;
|
||||||
|
const size_t ahb_window_size;
|
||||||
|
const size_t ahb_base;
|
||||||
|
const bool backing_is_shared;
|
||||||
DeviceInterface* device_inter;
|
DeviceInterface* device_inter;
|
||||||
|
|
||||||
struct TrackedEntry {
|
struct TrackedEntry {
|
||||||
|
|||||||
@@ -171,6 +171,11 @@ struct DeviceMemoryManagerAllocator {
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
||||||
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
||||||
|
, physical_size{device_memory_.buffer.BackingSize()}
|
||||||
|
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
|
||||||
|
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
|
||||||
|
, ahb_base{device_memory_.buffer.BackingHardwareBufferBase()}
|
||||||
|
, backing_is_shared{device_memory_.buffer.IsBackingShared()}
|
||||||
, device_inter{nullptr}
|
, device_inter{nullptr}
|
||||||
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
||||||
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
||||||
|
|||||||
@@ -35,20 +35,17 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||||
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||||
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||||
if (in == nullptr || ips == nullptr)
|
if (in == nullptr || ips == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
|
||||||
if (type == IPSFileType::Error)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
auto in_data = in->ReadAllBytes();
|
||||||
if (in_data.size() == 0)
|
auto const type = IdentifyMagic(in_data);
|
||||||
|
if (type == IPSFileType::Error)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||||
@@ -103,7 +100,8 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
|
|
||||||
|
|
||||||
struct IPSwitchRecord {
|
struct IPSwitchRecord {
|
||||||
std::vector<uint8_t> data;
|
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
||||||
|
size_t count;
|
||||||
};
|
};
|
||||||
struct IPSwitchCompiler::IPSwitchPatch {
|
struct IPSwitchCompiler::IPSwitchPatch {
|
||||||
::Common::unordered_map<u32, IPSwitchRecord> records;
|
::Common::unordered_map<u32, IPSwitchRecord> records;
|
||||||
@@ -124,23 +122,22 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
|||||||
IPSwitchRecord r{};
|
IPSwitchRecord r{};
|
||||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
r.data.push_back([it]() {
|
switch (it[1]) {
|
||||||
switch (it[1]) {
|
case 'a': r.data[r.count] = '\a'; break;
|
||||||
case 'a': return '\a';
|
case 'b': r.data[r.count] = '\b'; break;
|
||||||
case 'b': return '\b';
|
case 'e': r.data[r.count] = '\e'; break;
|
||||||
case 'e': return '\e';
|
case 'f': r.data[r.count] = '\f'; break;
|
||||||
case 'f': return '\f';
|
case 'n': r.data[r.count] = '\n'; break;
|
||||||
case 'n': return '\n';
|
case 'r': r.data[r.count] = '\r'; break;
|
||||||
case 'r': return '\r';
|
case 't': r.data[r.count] = '\t'; break;
|
||||||
case 't': return '\t';
|
case 'v': r.data[r.count] = '\v'; break;
|
||||||
case 'v': return '\v';
|
case '?': r.data[r.count] = '\?'; break;
|
||||||
case '?': return '\?';
|
default: r.data[r.count] = it[1]; break;
|
||||||
default: return it[1];
|
}
|
||||||
}
|
++r.count;
|
||||||
}());
|
|
||||||
it += 2;
|
it += 2;
|
||||||
} else {
|
} else {
|
||||||
r.data.push_back(*it);
|
++r.count;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +223,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
if (start <= line.cend() && end <= line.cend()) {
|
if (start <= line.cend() && end <= line.cend()) {
|
||||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
||||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
||||||
r.data.resize(hs.size());
|
|
||||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
std::memcpy(r.data.data(), hs.data(), hs.size());
|
||||||
|
r.count = hs.size();
|
||||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
||||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
||||||
} else {
|
} else {
|
||||||
@@ -296,7 +293,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
|||||||
if (patch.enabled) {
|
if (patch.enabled) {
|
||||||
for (const auto& record : patch.records) {
|
for (const auto& record : patch.records) {
|
||||||
if (record.first < in_data.size()) {
|
if (record.first < in_data.size()) {
|
||||||
auto replace_size = record.second.data.size();
|
auto replace_size = record.second.count;
|
||||||
if (record.first + replace_size > in_data.size())
|
if (record.first + replace_size > in_data.size())
|
||||||
replace_size = in_data.size() - record.first;
|
replace_size = in_data.size() - record.first;
|
||||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -157,15 +156,12 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
|||||||
NACP nacp{nacp_file};
|
NACP nacp{nacp_file};
|
||||||
return nacp.GetVersionString();
|
return nacp.GetVersionString();
|
||||||
}
|
}
|
||||||
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
|
|
||||||
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
|
|
||||||
}
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
PatchManager::PatchManager(u64 title_id_,
|
PatchManager::PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
const ContentProvider& content_provider_)
|
const ContentProvider& content_provider_)
|
||||||
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||||
|
|
||||||
PatchManager::~PatchManager() = default;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -173,35 +169,13 @@ u64 PatchManager::GetTitleID() const {
|
|||||||
return title_id;
|
return title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
|
|
||||||
const auto get_root = [&](u64 id) {
|
|
||||||
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
|
|
||||||
};
|
|
||||||
auto root = get_root(title_id);
|
|
||||||
if (!parent_title_id) {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
|
|
||||||
std::erase(roots, nullptr);
|
|
||||||
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> PatchManager::GetDisabledAddons() const {
|
|
||||||
auto disabled = Settings::values.disabled_addons[title_id];
|
|
||||||
if (parent_title_id) {
|
|
||||||
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
|
|
||||||
disabled.insert(disabled.end(), shared.begin(), shared.end());
|
|
||||||
}
|
|
||||||
return disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||||
|
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
return exefs;
|
||||||
|
|
||||||
const auto disabled = GetDisabledAddons();
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -329,8 +303,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = GetModificationLoadRoot();
|
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||||
if (load_dir != nullptr) {
|
if (load_dir != nullptr) {
|
||||||
@@ -372,7 +346,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||||
const auto disabled = GetDisabledAddons();
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
@@ -431,7 +405,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||||
|
|
||||||
const auto load_dir = GetModificationLoadRoot();
|
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return nso;
|
return nso;
|
||||||
@@ -474,7 +448,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||||
|
|
||||||
const auto load_dir = GetModificationLoadRoot();
|
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return false;
|
return false;
|
||||||
@@ -488,13 +462,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||||
const auto load_dir = GetModificationLoadRoot();
|
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto disabled = GetDisabledAddons();
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
auto patch_dirs = load_dir->GetSubdirectories();
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||||
|
|
||||||
@@ -528,16 +502,17 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
||||||
const auto load_dir = GetModificationLoadRoot();
|
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||||
|
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||||
type != ContentRecordType::HtmlDocument) ||
|
type != ContentRecordType::HtmlDocument) ||
|
||||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto disabled = GetDisabledAddons();
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||||
patch_dirs.push_back(sdmc_load_dir);
|
patch_dirs.push_back(sdmc_load_dir);
|
||||||
@@ -616,7 +591,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
const auto disabled = GetDisabledAddons();
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -723,7 +698,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, type);
|
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -1097,22 +1072,15 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||||
const auto version = content_provider.GetEntryVersion(update_tid);
|
return content_provider.GetEntryVersion(update_tid);
|
||||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto version = content_provider.GetEntryVersion(title_id);
|
return content_provider.GetEntryVersion(title_id);
|
||||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||||
if (base_control_nca == nullptr) {
|
if (base_control_nca == nullptr) {
|
||||||
if (parent_title_id) {
|
|
||||||
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
|
|
||||||
const PatchManager parent{control_id, fs_controller, content_provider};
|
|
||||||
return parent.GetControlMetadata();
|
|
||||||
}
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,14 +109,10 @@ public:
|
|||||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
|
|
||||||
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
|
|
||||||
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
|
|
||||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const;
|
const std::string& build_id) const;
|
||||||
|
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
std::optional<u64> parent_title_id;
|
|
||||||
const Service::FileSystem::FileSystemController& fs_controller;
|
const Service::FileSystem::FileSystemController& fs_controller;
|
||||||
const ContentProvider& content_provider;
|
const ContentProvider& content_provider;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
@@ -349,22 +348,6 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
|||||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
|
|
||||||
const auto application_id = GetBaseTitleID(program_id);
|
|
||||||
const auto program_index = program_id - application_id;
|
|
||||||
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|
|
||||||
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
|
|
||||||
return application_id;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||||
|
|
||||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||||
|
|||||||
@@ -100,8 +100,6 @@ public:
|
|||||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||||
std::optional<u64> title_id = {}) const = 0;
|
std::optional<u64> title_id = {}) const = 0;
|
||||||
|
|
||||||
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// A single instance of KeyManager to be used by GetEntry()
|
// A single instance of KeyManager to be used by GetEntry()
|
||||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "hid_core/frontend/emulated_controller.h"
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
#include "hid_core/hid_core.h"
|
#include "hid_core/hid_core.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
@@ -30,39 +29,23 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
|||||||
|
|
||||||
const std::size_t min_supported_players =
|
const std::size_t min_supported_players =
|
||||||
parameters.enable_single_mode ? 1 : parameters.min_players;
|
parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||||
using Core::HID::NpadStyleIndex;
|
|
||||||
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
|
||||||
std::size_t num_selected_players = 0;
|
|
||||||
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
|
|
||||||
|
|
||||||
// reserve existing AND valid players before filling slots. include Handheld, but not other
|
// Disconnect Handheld first.
|
||||||
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
|
|
||||||
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
|
||||||
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
|
|
||||||
|
|
||||||
const auto style = controller->GetNpadStyleIndex();
|
|
||||||
keep_connected[index] =
|
|
||||||
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
|
|
||||||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
|
|
||||||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
|
|
||||||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
|
|
||||||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
|
|
||||||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
|
|
||||||
num_selected_players += keep_connected[index];
|
|
||||||
}
|
|
||||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
|
handheld->Disconnect();
|
||||||
|
|
||||||
// Deduce the best configuration based on the input parameters.
|
// Deduce the best configuration based on the input parameters.
|
||||||
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
||||||
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
if (keep_connected[index]) continue;
|
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
||||||
|
// This makes it easy to connect the desired controllers.
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
|
|
||||||
// only add players still needed to reach the minimum
|
// Only connect the minimum number of required players.
|
||||||
if (num_selected_players >= min_supported_players) continue;
|
if (index >= min_supported_players) {
|
||||||
++num_selected_players;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
|
|||||||
|
|
||||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
|
||||||
ASSERT(this->IsLocked());
|
ASSERT(this->IsLocked());
|
||||||
for (auto* thread : m_woken_dummy_threads)
|
if (m_woken_dummy_threads.size() > 0) {
|
||||||
thread->DummyThreadEndWait(kernel);
|
for (auto* thread : m_woken_dummy_threads)
|
||||||
m_woken_dummy_threads.clear();
|
thread->DummyThreadEndWait(kernel);
|
||||||
|
m_woken_dummy_threads.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
|
|||||||
KThread* cur_child = nullptr;
|
KThread* cur_child = nullptr;
|
||||||
{
|
{
|
||||||
KScopedLightLock proc_lk(process->GetListLock());
|
KScopedLightLock proc_lk(process->GetListLock());
|
||||||
|
|
||||||
auto& thread_list = process->GetThreadList();
|
auto& thread_list = process->GetThreadList();
|
||||||
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
|
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
|
||||||
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
|
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
|
||||||
|
|||||||
@@ -465,10 +465,6 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
|
|||||||
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
||||||
// retry.
|
// retry.
|
||||||
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
||||||
// Some libc++ lazily init mutex
|
|
||||||
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
|
|
||||||
DEBUG_ASSERT(!can_lock);
|
|
||||||
|
|
||||||
// Our switch failed.
|
// Our switch failed.
|
||||||
// We should unlock the thread context, and then retry.
|
// We should unlock the thread context, and then retry.
|
||||||
highest_priority_thread->m_context_guard.unlock();
|
highest_priority_thread->m_context_guard.unlock();
|
||||||
@@ -500,10 +496,6 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
|
|||||||
|
|
||||||
// Check if the thread is terminated by checking the DPC flags.
|
// Check if the thread is terminated by checking the DPC flags.
|
||||||
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
||||||
// Some libc++ lazily init mutex
|
|
||||||
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
|
|
||||||
DEBUG_ASSERT(!can_lock);
|
|
||||||
|
|
||||||
// The thread isn't terminated, so we want to unlock it.
|
// The thread isn't terminated, so we want to unlock it.
|
||||||
thread->m_context_guard.unlock();
|
thread->m_context_guard.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/atomic_ops.h"
|
#include "common/atomic_ops.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/spin_lock.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KSlabHeapImpl() = default;
|
constexpr KSlabHeapImpl() = default;
|
||||||
|
|
||||||
void Initialize() {
|
void Initialize() {
|
||||||
ASSERT(m_head == nullptr);
|
ASSERT(m_head == nullptr);
|
||||||
@@ -67,7 +68,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<Node*> m_head{};
|
std::atomic<Node*> m_head{};
|
||||||
std::mutex m_lock;
|
Common::SpinLock m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|||||||
@@ -1236,13 +1236,18 @@ namespace Kernel {
|
|||||||
KScopedSchedulerLock sl{kernel};
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
// Determine if this is the first termination request.
|
// Determine if this is the first termination request.
|
||||||
// Perform an atomic compare-and-swap from false to true.
|
const bool first_request = [&]() -> bool {
|
||||||
bool expected = false;
|
// Perform an atomic compare-and-swap from false to true.
|
||||||
|
bool expected = false;
|
||||||
|
return m_termination_requested.compare_exchange_strong(expected, true);
|
||||||
|
}();
|
||||||
|
|
||||||
// If this is the first request, start termination procedure.
|
// If this is the first request, start termination procedure.
|
||||||
if (m_termination_requested.compare_exchange_strong(expected, true)) {
|
if (first_request) {
|
||||||
// If the thread is in initialized state, just change state to terminated.
|
// If the thread is in initialized state, just change state to terminated.
|
||||||
if (this->GetState() == ThreadState::Initialized) {
|
if (this->GetState() == ThreadState::Initialized) {
|
||||||
return m_thread_state = ThreadState::Terminated;
|
m_thread_state = ThreadState::Terminated;
|
||||||
|
return ThreadState::Terminated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the terminating dpc.
|
// Register the terminating dpc.
|
||||||
@@ -1276,6 +1281,7 @@ namespace Kernel {
|
|||||||
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
|
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->GetState();
|
return this->GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
|
#include "common/spin_lock.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/hle/kernel/k_affinity_mask.h"
|
#include "core/hle/kernel/k_affinity_mask.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
@@ -919,7 +920,7 @@ private:
|
|||||||
bool m_resource_limit_release_hint{};
|
bool m_resource_limit_release_hint{};
|
||||||
bool m_is_kernel_address_key{};
|
bool m_is_kernel_address_key{};
|
||||||
StackParameters m_stack_parameters{};
|
StackParameters m_stack_parameters{};
|
||||||
std::mutex m_context_guard{};
|
Common::SpinLock m_context_guard{};
|
||||||
|
|
||||||
// For emulation
|
// For emulation
|
||||||
std::shared_ptr<Common::Fiber> m_host_context{};
|
std::shared_ptr<Common::Fiber> m_host_context{};
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/thread.h"
|
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/k_worker_task.h"
|
#include "core/hle/kernel/k_worker_task.h"
|
||||||
@@ -28,33 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KWorkerTaskManager::KWorkerTaskManager(KernelCore& kernel) {
|
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
|
||||||
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("KWorkerManager");
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
KWorkerTask* t;
|
|
||||||
{
|
|
||||||
std::unique_lock lk{m_task_mutex};
|
|
||||||
m_task_cv.wait(lk, m_waiting_thread.get_stop_token(), [&]() {
|
|
||||||
return !m_task_queue.empty();
|
|
||||||
});
|
|
||||||
if (stop_token.stop_requested())
|
|
||||||
break;
|
|
||||||
t = m_task_queue.back();
|
|
||||||
m_task_queue.pop_back();
|
|
||||||
}
|
|
||||||
t->DoWorkerTask(kernel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
KWorkerTaskManager::~KWorkerTaskManager() {
|
|
||||||
if (m_waiting_thread.joinable()) {
|
|
||||||
m_waiting_thread.request_stop();
|
|
||||||
m_task_cv.notify_one();
|
|
||||||
m_waiting_thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
||||||
ASSERT(type <= WorkerType::Count);
|
ASSERT(type <= WorkerType::Count);
|
||||||
@@ -63,11 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
|
|||||||
|
|
||||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
||||||
KScopedSchedulerLock sl(kernel);
|
KScopedSchedulerLock sl(kernel);
|
||||||
{
|
m_waiting_thread.QueueWork([&kernel, task]() {
|
||||||
std::scoped_lock lk{m_task_mutex};
|
// Do the task.
|
||||||
m_task_queue.emplace_back(task);
|
task->DoWorkerTask(kernel);
|
||||||
}
|
});
|
||||||
m_task_cv.notify_one();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <thread>
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
|
|
||||||
@@ -23,17 +18,15 @@ public:
|
|||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
KWorkerTaskManager(KernelCore& kernel);
|
KWorkerTaskManager();
|
||||||
~KWorkerTaskManager();
|
|
||||||
|
|
||||||
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
|
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
||||||
std::jthread m_waiting_thread;
|
|
||||||
|
|
||||||
std::mutex m_task_mutex;
|
private:
|
||||||
std::condition_variable_any m_task_cv;
|
Common::ThreadWorker m_waiting_thread;
|
||||||
std::vector<KWorkerTask*> m_task_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -80,10 +80,7 @@ struct KernelCore::Impl {
|
|||||||
// so it will be statically given a TLS slot anyways.
|
// so it will be statically given a TLS slot anyways.
|
||||||
static inline thread_local ThreadLocalData tls_data = {};
|
static inline thread_local ThreadLocalData tls_data = {};
|
||||||
|
|
||||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
|
||||||
: worker_task_manager{kernel_}
|
|
||||||
, system{system_}
|
|
||||||
{
|
|
||||||
tls_data.lock = true;
|
tls_data.lock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
|
|||||||
return *impl->hardware_timer;
|
return *impl->hardware_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
|
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
|
||||||
if (!impl->global_object_list_container)
|
return *impl->global_object_list_container;
|
||||||
return nullptr;
|
|
||||||
return std::addressof(*impl->global_object_list_container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
|
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
|
||||||
if (!impl->global_object_list_container)
|
return *impl->global_object_list_container;
|
||||||
return nullptr;
|
|
||||||
return std::addressof(*impl->global_object_list_container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::PrepareReschedule(std::size_t id) {
|
void KernelCore::PrepareReschedule(std::size_t id) {
|
||||||
@@ -1008,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
|||||||
|
|
||||||
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
||||||
auto* manager = server_manager.get();
|
auto* manager = server_manager.get();
|
||||||
if (!impl->is_shutting_down) {
|
|
||||||
|
{
|
||||||
std::scoped_lock lk{impl->server_lock};
|
std::scoped_lock lk{impl->server_lock};
|
||||||
|
if (impl->is_shutting_down) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
impl->server_managers.emplace_back(std::move(server_manager));
|
impl->server_managers.emplace_back(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
manager->LoopProcess();
|
manager->LoopProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::ShutdownCores() {
|
void KernelCore::ShutdownCores() {
|
||||||
// Notify shutdown (pre-emptively)
|
|
||||||
for (auto& sm : impl->server_managers) {
|
|
||||||
sm->NotifyShutdown();
|
|
||||||
}
|
|
||||||
impl->TerminateAllProcesses();
|
impl->TerminateAllProcesses();
|
||||||
KScopedSchedulerLock lk{*this};
|
KScopedSchedulerLock lk{*this};
|
||||||
for (auto* thread : impl->shutdown_threads)
|
for (auto* thread : impl->shutdown_threads)
|
||||||
|
|||||||
@@ -175,8 +175,9 @@ public:
|
|||||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||||
void PrepareReschedule(std::size_t id);
|
void PrepareReschedule(std::size_t id);
|
||||||
|
|
||||||
KAutoObjectWithListContainer* ObjectListContainer();
|
KAutoObjectWithListContainer& ObjectListContainer();
|
||||||
const KAutoObjectWithListContainer* ObjectListContainer() const;
|
|
||||||
|
const KAutoObjectWithListContainer& ObjectListContainer() const;
|
||||||
|
|
||||||
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
||||||
/// leaks after emulation has been shutdown.
|
/// leaks after emulation has been shutdown.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user