mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 19:26:52 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d336341c8d | |||
| a4fd5c6b0b | |||
| ef7e9c00b0 | |||
| 3be0db1266 | |||
| 815325ccec | |||
| 37fe911952 | |||
| f273423b2b | |||
| 87d2f03c39 |
@@ -333,6 +333,11 @@
|
||||
"repo": "herumi/xbyak",
|
||||
"version": "v7.40.1"
|
||||
},
|
||||
"zbic": {
|
||||
"hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482",
|
||||
"repo": "kinnay/zbic",
|
||||
"version": "11b08f2712264bbed731545085cbd9702096ceb7"
|
||||
},
|
||||
"zlib": {
|
||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||
"min_version": "1.2",
|
||||
|
||||
Vendored
+5
@@ -48,6 +48,11 @@ if (NOT TARGET stb::headers)
|
||||
add_library(stb::headers ALIAS stb)
|
||||
endif()
|
||||
|
||||
AddJsonPackage(NAME zbic DOWNLOAD_ONLY)
|
||||
set(ZBIC_INCLUDE_DIR
|
||||
"${zbic_SOURCE_DIR}/src"
|
||||
PARENT_SCOPE)
|
||||
|
||||
# ItaniumDemangle (Windows only)
|
||||
if (WIN32 AND NOT TARGET LLVM::Demangle)
|
||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||
|
||||
-1
@@ -30,7 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
|
||||
-7
@@ -908,13 +908,6 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
|
||||
-1
@@ -549,7 +549,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -580,8 +580,6 @@
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||
|
||||
@@ -1099,7 +1097,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||
<string name="gpu_fence_behavior_strict">صارم</string>
|
||||
|
||||
<string name="vram_usage_conservative">محافظ</string>
|
||||
<string name="vram_usage_aggressive">عدواني</string>
|
||||
|
||||
@@ -967,7 +967,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||
<string name="gpu_fence_behavior_strict">Strikt</string>
|
||||
|
||||
<string name="vram_usage_conservative">Konservativ</string>
|
||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||
|
||||
@@ -524,8 +524,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||
|
||||
@@ -1038,7 +1036,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||
<string name="gpu_fence_behavior_strict">Estricto</string>
|
||||
|
||||
<string name="vram_usage_conservative">Conservador</string>
|
||||
<string name="vram_usage_aggressive">Agresivo</string>
|
||||
|
||||
@@ -569,8 +569,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||
|
||||
@@ -1088,7 +1086,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||
<string name="gpu_fence_behavior_strict">Строгий</string>
|
||||
|
||||
<string name="vram_usage_conservative">Консервативный</string>
|
||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||
|
||||
@@ -570,8 +570,6 @@
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||
|
||||
@@ -1089,7 +1087,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||
<string name="gpu_fence_behavior_strict">严格</string>
|
||||
|
||||
<string name="vram_usage_conservative">保守式</string>
|
||||
<string name="vram_usage_aggressive">主动式</string>
|
||||
|
||||
@@ -561,8 +561,6 @@
|
||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
||||
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||
|
||||
|
||||
@@ -558,7 +558,6 @@
|
||||
<item>@string/gpu_fence_behavior_immediate</item>
|
||||
<item>@string/gpu_fence_behavior_balanced</item>
|
||||
<item>@string/gpu_fence_behavior_accurate</item>
|
||||
<item>@string/gpu_fence_behavior_strict</item>
|
||||
</string-array>
|
||||
<integer-array name="gpuFenceBehaviorValues">
|
||||
<item>0</item>
|
||||
|
||||
@@ -586,8 +586,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
@@ -1138,7 +1136,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||
<string name="gpu_fence_behavior_strict">Strict</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
|
||||
@@ -62,7 +62,6 @@ add_library(
|
||||
fs/fs_util.h
|
||||
fs/path_util.cpp
|
||||
fs/path_util.h
|
||||
hash.h
|
||||
heap_tracker.cpp
|
||||
heap_tracker.h
|
||||
hex_util.cpp
|
||||
@@ -137,6 +136,8 @@ add_library(
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
zbic_compression.cpp
|
||||
zbic_compression.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
@@ -147,6 +148,10 @@ add_library(
|
||||
net/net.h net/net.cpp
|
||||
container/unordered_map.h container/unordered_set.h)
|
||||
|
||||
set_source_files_properties(zbic_compression.cpp PROPERTIES
|
||||
INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}"
|
||||
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:Clang,GNU>:-Wno-unused-function;-Wno-missing-declarations;-Wno-shadow>")
|
||||
|
||||
if(WIN32)
|
||||
target_sources(common PRIVATE windows/timer_resolution.cpp
|
||||
windows/timer_resolution.h)
|
||||
|
||||
+252
-251
@@ -12,93 +12,94 @@
|
||||
#include "common/android/multiplayer/multiplayer.h"
|
||||
#include <network/network.h>
|
||||
|
||||
static struct {
|
||||
JavaVM *java_vm;
|
||||
jclass native_library_class;
|
||||
jclass disk_cache_progress_class;
|
||||
jclass load_callback_stage_class;
|
||||
jclass game_dir_class;
|
||||
jmethodID game_dir_constructor;
|
||||
jmethodID exit_emulation_activity;
|
||||
jmethodID disk_cache_load_progress;
|
||||
jmethodID on_emulation_started;
|
||||
jmethodID on_emulation_stopped;
|
||||
jmethodID on_program_changed;
|
||||
jmethodID copy_to_storage;
|
||||
jmethodID file_exists;
|
||||
jmethodID file_extension;
|
||||
|
||||
static JavaVM *s_java_vm;
|
||||
static jclass s_native_library_class;
|
||||
static jclass s_disk_cache_progress_class;
|
||||
static jclass s_load_callback_stage_class;
|
||||
static jclass s_game_dir_class;
|
||||
static jmethodID s_game_dir_constructor;
|
||||
static jmethodID s_exit_emulation_activity;
|
||||
static jmethodID s_disk_cache_load_progress;
|
||||
static jmethodID s_on_emulation_started;
|
||||
static jmethodID s_on_emulation_stopped;
|
||||
static jmethodID s_on_program_changed;
|
||||
static jmethodID s_copy_to_storage;
|
||||
static jmethodID s_file_exists;
|
||||
static jmethodID s_file_extension;
|
||||
jclass game_class;
|
||||
jmethodID game_constructor;
|
||||
jfieldID game_title_field;
|
||||
jfieldID game_path_field;
|
||||
jfieldID game_program_id_field;
|
||||
jfieldID game_developer_field;
|
||||
jfieldID game_version_field;
|
||||
jfieldID game_is_homebrew_field;
|
||||
|
||||
static jclass s_game_class;
|
||||
static jmethodID s_game_constructor;
|
||||
static jfieldID s_game_title_field;
|
||||
static jfieldID s_game_path_field;
|
||||
static jfieldID s_game_program_id_field;
|
||||
static jfieldID s_game_developer_field;
|
||||
static jfieldID s_game_version_field;
|
||||
static jfieldID s_game_is_homebrew_field;
|
||||
jclass string_class;
|
||||
jclass pair_class;
|
||||
jmethodID pair_constructor;
|
||||
jfieldID pair_first_field;
|
||||
jfieldID pair_second_field;
|
||||
|
||||
static jclass s_string_class;
|
||||
static jclass s_pair_class;
|
||||
static jmethodID s_pair_constructor;
|
||||
static jfieldID s_pair_first_field;
|
||||
static jfieldID s_pair_second_field;
|
||||
jclass overlay_control_data_class;
|
||||
jmethodID overlay_control_data_constructor;
|
||||
jfieldID overlay_control_data_id_field;
|
||||
jfieldID overlay_control_data_enabled_field;
|
||||
jfieldID overlay_control_data_individual_scale_field;
|
||||
jfieldID overlay_control_data_landscape_position_field;
|
||||
jfieldID overlay_control_data_portrait_position_field;
|
||||
jfieldID overlay_control_data_foldable_position_field;
|
||||
|
||||
static jclass s_overlay_control_data_class;
|
||||
static jmethodID s_overlay_control_data_constructor;
|
||||
static jfieldID s_overlay_control_data_id_field;
|
||||
static jfieldID s_overlay_control_data_enabled_field;
|
||||
static jfieldID s_overlay_control_data_individual_scale_field;
|
||||
static jfieldID s_overlay_control_data_landscape_position_field;
|
||||
static jfieldID s_overlay_control_data_portrait_position_field;
|
||||
static jfieldID s_overlay_control_data_foldable_position_field;
|
||||
jclass patch_class;
|
||||
jmethodID patch_constructor;
|
||||
jfieldID patch_enabled_field;
|
||||
jfieldID patch_name_field;
|
||||
jfieldID patch_version_field;
|
||||
jfieldID patch_type_field;
|
||||
jfieldID patch_program_id_field;
|
||||
jfieldID patch_title_id_field;
|
||||
|
||||
static jclass s_patch_class;
|
||||
static jmethodID s_patch_constructor;
|
||||
static jfieldID s_patch_enabled_field;
|
||||
static jfieldID s_patch_name_field;
|
||||
static jfieldID s_patch_version_field;
|
||||
static jfieldID s_patch_type_field;
|
||||
static jfieldID s_patch_program_id_field;
|
||||
static jfieldID s_patch_title_id_field;
|
||||
jclass double_class;
|
||||
jmethodID double_constructor;
|
||||
jmethodID double_value_method;
|
||||
|
||||
static jclass s_double_class;
|
||||
static jmethodID s_double_constructor;
|
||||
static jmethodID s_double_value_method;
|
||||
jclass integer_class;
|
||||
jmethodID integer_constructor;
|
||||
jmethodID integer_value_method;
|
||||
|
||||
static jclass s_integer_class;
|
||||
static jmethodID s_integer_constructor;
|
||||
static jmethodID s_integer_value_method;
|
||||
jclass boolean_class;
|
||||
jmethodID boolean_constructor;
|
||||
jmethodID boolean_value_method;
|
||||
|
||||
static jclass s_boolean_class;
|
||||
static jmethodID s_boolean_constructor;
|
||||
static jmethodID s_boolean_value_method;
|
||||
jclass player_input_class;
|
||||
jmethodID player_input_constructor;
|
||||
jfieldID player_input_connected_field;
|
||||
jfieldID player_input_buttons_field;
|
||||
jfieldID player_input_analogs_field;
|
||||
jfieldID player_input_motions_field;
|
||||
jfieldID player_input_vibration_enabled_field;
|
||||
jfieldID player_input_vibration_strength_field;
|
||||
jfieldID player_input_body_color_left_field;
|
||||
jfieldID player_input_body_color_right_field;
|
||||
jfieldID player_input_button_color_left_field;
|
||||
jfieldID player_input_button_color_right_field;
|
||||
jfieldID player_input_profile_name_field;
|
||||
jfieldID player_input_use_system_vibrator_field;
|
||||
|
||||
static jclass s_player_input_class;
|
||||
static jmethodID s_player_input_constructor;
|
||||
static jfieldID s_player_input_connected_field;
|
||||
static jfieldID s_player_input_buttons_field;
|
||||
static jfieldID s_player_input_analogs_field;
|
||||
static jfieldID s_player_input_motions_field;
|
||||
static jfieldID s_player_input_vibration_enabled_field;
|
||||
static jfieldID s_player_input_vibration_strength_field;
|
||||
static jfieldID s_player_input_body_color_left_field;
|
||||
static jfieldID s_player_input_body_color_right_field;
|
||||
static jfieldID s_player_input_button_color_left_field;
|
||||
static jfieldID s_player_input_button_color_right_field;
|
||||
static jfieldID s_player_input_profile_name_field;
|
||||
static jfieldID s_player_input_use_system_vibrator_field;
|
||||
jclass yuzu_input_device_interface;
|
||||
jmethodID yuzu_input_device_get_name;
|
||||
jmethodID yuzu_input_device_get_guid;
|
||||
jmethodID yuzu_input_device_get_port;
|
||||
jmethodID yuzu_input_device_get_supports_vibration;
|
||||
jmethodID yuzu_input_device_vibrate;
|
||||
jmethodID yuzu_input_device_get_axes;
|
||||
jmethodID yuzu_input_device_has_keys;
|
||||
|
||||
static jclass s_yuzu_input_device_interface;
|
||||
static jmethodID s_yuzu_input_device_get_name;
|
||||
static jmethodID s_yuzu_input_device_get_guid;
|
||||
static jmethodID s_yuzu_input_device_get_port;
|
||||
static jmethodID s_yuzu_input_device_get_supports_vibration;
|
||||
static jmethodID s_yuzu_input_device_vibrate;
|
||||
static jmethodID s_yuzu_input_device_get_axes;
|
||||
static jmethodID s_yuzu_input_device_has_keys;
|
||||
|
||||
static jmethodID s_add_netplay_message;
|
||||
static jmethodID s_clear_chat;
|
||||
jmethodID add_netplay_message;
|
||||
jmethodID clear_chat;
|
||||
} state;
|
||||
|
||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
|
||||
@@ -106,14 +107,14 @@ namespace Common::Android {
|
||||
JNIEnv *GetEnvForThread() {
|
||||
thread_local static struct OwnedEnv {
|
||||
OwnedEnv() {
|
||||
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (status == JNI_EDETACHED)
|
||||
s_java_vm->AttachCurrentThread(&env, nullptr);
|
||||
state.java_vm->AttachCurrentThread(&env, nullptr);
|
||||
}
|
||||
|
||||
~OwnedEnv() {
|
||||
if (status == JNI_EDETACHED)
|
||||
s_java_vm->DetachCurrentThread();
|
||||
state.java_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
int status;
|
||||
@@ -123,303 +124,303 @@ namespace Common::Android {
|
||||
}
|
||||
|
||||
jclass GetNativeLibraryClass() {
|
||||
return s_native_library_class;
|
||||
return state.native_library_class;
|
||||
}
|
||||
|
||||
jclass GetDiskCacheProgressClass() {
|
||||
return s_disk_cache_progress_class;
|
||||
return state.disk_cache_progress_class;
|
||||
}
|
||||
|
||||
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||
return s_load_callback_stage_class;
|
||||
return state.load_callback_stage_class;
|
||||
}
|
||||
|
||||
jclass GetGameDirClass() {
|
||||
return s_game_dir_class;
|
||||
return state.game_dir_class;
|
||||
}
|
||||
|
||||
jmethodID GetGameDirConstructor() {
|
||||
return s_game_dir_constructor;
|
||||
return state.game_dir_constructor;
|
||||
}
|
||||
|
||||
jmethodID GetExitEmulationActivity() {
|
||||
return s_exit_emulation_activity;
|
||||
return state.exit_emulation_activity;
|
||||
}
|
||||
|
||||
jmethodID GetDiskCacheLoadProgress() {
|
||||
return s_disk_cache_load_progress;
|
||||
return state.disk_cache_load_progress;
|
||||
}
|
||||
|
||||
jmethodID GetCopyToStorage() {
|
||||
return s_copy_to_storage;
|
||||
return state.copy_to_storage;
|
||||
}
|
||||
|
||||
jmethodID GetFileExists() {
|
||||
return s_file_exists;
|
||||
return state.file_exists;
|
||||
}
|
||||
|
||||
jmethodID GetFileExtension() {
|
||||
return s_file_extension;
|
||||
return state.file_extension;
|
||||
}
|
||||
|
||||
jmethodID GetOnEmulationStarted() {
|
||||
return s_on_emulation_started;
|
||||
return state.on_emulation_started;
|
||||
}
|
||||
|
||||
jmethodID GetOnEmulationStopped() {
|
||||
return s_on_emulation_stopped;
|
||||
return state.on_emulation_stopped;
|
||||
}
|
||||
|
||||
jmethodID GetOnProgramChanged() {
|
||||
return s_on_program_changed;
|
||||
return state.on_program_changed;
|
||||
}
|
||||
|
||||
jclass GetGameClass() {
|
||||
return s_game_class;
|
||||
return state.game_class;
|
||||
}
|
||||
|
||||
jmethodID GetGameConstructor() {
|
||||
return s_game_constructor;
|
||||
return state.game_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetGameTitleField() {
|
||||
return s_game_title_field;
|
||||
return state.game_title_field;
|
||||
}
|
||||
|
||||
jfieldID GetGamePathField() {
|
||||
return s_game_path_field;
|
||||
return state.game_path_field;
|
||||
}
|
||||
|
||||
jfieldID GetGameProgramIdField() {
|
||||
return s_game_program_id_field;
|
||||
return state.game_program_id_field;
|
||||
}
|
||||
|
||||
jfieldID GetGameDeveloperField() {
|
||||
return s_game_developer_field;
|
||||
return state.game_developer_field;
|
||||
}
|
||||
|
||||
jfieldID GetGameVersionField() {
|
||||
return s_game_version_field;
|
||||
return state.game_version_field;
|
||||
}
|
||||
|
||||
jfieldID GetGameIsHomebrewField() {
|
||||
return s_game_is_homebrew_field;
|
||||
return state.game_is_homebrew_field;
|
||||
}
|
||||
|
||||
jclass GetStringClass() {
|
||||
return s_string_class;
|
||||
return state.string_class;
|
||||
}
|
||||
|
||||
jclass GetPairClass() {
|
||||
return s_pair_class;
|
||||
return state.pair_class;
|
||||
}
|
||||
|
||||
jmethodID GetPairConstructor() {
|
||||
return s_pair_constructor;
|
||||
return state.pair_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetPairFirstField() {
|
||||
return s_pair_first_field;
|
||||
return state.pair_first_field;
|
||||
}
|
||||
|
||||
jfieldID GetPairSecondField() {
|
||||
return s_pair_second_field;
|
||||
return state.pair_second_field;
|
||||
}
|
||||
|
||||
jclass GetOverlayControlDataClass() {
|
||||
return s_overlay_control_data_class;
|
||||
return state.overlay_control_data_class;
|
||||
}
|
||||
|
||||
jmethodID GetOverlayControlDataConstructor() {
|
||||
return s_overlay_control_data_constructor;
|
||||
return state.overlay_control_data_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataIdField() {
|
||||
return s_overlay_control_data_id_field;
|
||||
return state.overlay_control_data_id_field;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataEnabledField() {
|
||||
return s_overlay_control_data_enabled_field;
|
||||
return state.overlay_control_data_enabled_field;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataIndividualScaleField() {
|
||||
return s_overlay_control_data_individual_scale_field;
|
||||
return state.overlay_control_data_individual_scale_field;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataLandscapePositionField() {
|
||||
return s_overlay_control_data_landscape_position_field;
|
||||
return state.overlay_control_data_landscape_position_field;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataPortraitPositionField() {
|
||||
return s_overlay_control_data_portrait_position_field;
|
||||
return state.overlay_control_data_portrait_position_field;
|
||||
}
|
||||
|
||||
jfieldID GetOverlayControlDataFoldablePositionField() {
|
||||
return s_overlay_control_data_foldable_position_field;
|
||||
return state.overlay_control_data_foldable_position_field;
|
||||
}
|
||||
|
||||
jclass GetPatchClass() {
|
||||
return s_patch_class;
|
||||
return state.patch_class;
|
||||
}
|
||||
|
||||
jmethodID GetPatchConstructor() {
|
||||
return s_patch_constructor;
|
||||
return state.patch_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetPatchEnabledField() {
|
||||
return s_patch_enabled_field;
|
||||
return state.patch_enabled_field;
|
||||
}
|
||||
|
||||
jfieldID GetPatchNameField() {
|
||||
return s_patch_name_field;
|
||||
return state.patch_name_field;
|
||||
}
|
||||
|
||||
jfieldID GetPatchVersionField() {
|
||||
return s_patch_version_field;
|
||||
return state.patch_version_field;
|
||||
}
|
||||
|
||||
jfieldID GetPatchTypeField() {
|
||||
return s_patch_type_field;
|
||||
return state.patch_type_field;
|
||||
}
|
||||
|
||||
jfieldID GetPatchProgramIdField() {
|
||||
return s_patch_program_id_field;
|
||||
return state.patch_program_id_field;
|
||||
}
|
||||
|
||||
jfieldID GetPatchTitleIdField() {
|
||||
return s_patch_title_id_field;
|
||||
return state.patch_title_id_field;
|
||||
}
|
||||
|
||||
jclass GetDoubleClass() {
|
||||
return s_double_class;
|
||||
return state.double_class;
|
||||
}
|
||||
|
||||
jmethodID GetDoubleConstructor() {
|
||||
return s_double_constructor;
|
||||
return state.double_constructor;
|
||||
}
|
||||
|
||||
jmethodID GetDoubleValueMethod() {
|
||||
return s_double_value_method;
|
||||
return state.double_value_method;
|
||||
}
|
||||
|
||||
jclass GetIntegerClass() {
|
||||
return s_integer_class;
|
||||
return state.integer_class;
|
||||
}
|
||||
|
||||
jmethodID GetIntegerConstructor() {
|
||||
return s_integer_constructor;
|
||||
return state.integer_constructor;
|
||||
}
|
||||
|
||||
jmethodID GetIntegerValueMethod() {
|
||||
return s_integer_value_method;
|
||||
return state.integer_value_method;
|
||||
}
|
||||
|
||||
jclass GetBooleanClass() {
|
||||
return s_boolean_class;
|
||||
return state.boolean_class;
|
||||
}
|
||||
|
||||
jmethodID GetBooleanConstructor() {
|
||||
return s_boolean_constructor;
|
||||
return state.boolean_constructor;
|
||||
}
|
||||
|
||||
jmethodID GetBooleanValueMethod() {
|
||||
return s_boolean_value_method;
|
||||
return state.boolean_value_method;
|
||||
}
|
||||
|
||||
jclass GetPlayerInputClass() {
|
||||
return s_player_input_class;
|
||||
return state.player_input_class;
|
||||
}
|
||||
|
||||
jmethodID GetPlayerInputConstructor() {
|
||||
return s_player_input_constructor;
|
||||
return state.player_input_constructor;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputConnectedField() {
|
||||
return s_player_input_connected_field;
|
||||
return state.player_input_connected_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputButtonsField() {
|
||||
return s_player_input_buttons_field;
|
||||
return state.player_input_buttons_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputAnalogsField() {
|
||||
return s_player_input_analogs_field;
|
||||
return state.player_input_analogs_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputMotionsField() {
|
||||
return s_player_input_motions_field;
|
||||
return state.player_input_motions_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputVibrationEnabledField() {
|
||||
return s_player_input_vibration_enabled_field;
|
||||
return state.player_input_vibration_enabled_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputVibrationStrengthField() {
|
||||
return s_player_input_vibration_strength_field;
|
||||
return state.player_input_vibration_strength_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputBodyColorLeftField() {
|
||||
return s_player_input_body_color_left_field;
|
||||
return state.player_input_body_color_left_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputBodyColorRightField() {
|
||||
return s_player_input_body_color_right_field;
|
||||
return state.player_input_body_color_right_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputButtonColorLeftField() {
|
||||
return s_player_input_button_color_left_field;
|
||||
return state.player_input_button_color_left_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputButtonColorRightField() {
|
||||
return s_player_input_button_color_right_field;
|
||||
return state.player_input_button_color_right_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputProfileNameField() {
|
||||
return s_player_input_profile_name_field;
|
||||
return state.player_input_profile_name_field;
|
||||
}
|
||||
|
||||
jfieldID GetPlayerInputUseSystemVibratorField() {
|
||||
return s_player_input_use_system_vibrator_field;
|
||||
return state.player_input_use_system_vibrator_field;
|
||||
}
|
||||
|
||||
jclass GetYuzuInputDeviceInterface() {
|
||||
return s_yuzu_input_device_interface;
|
||||
return state.yuzu_input_device_interface;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceGetName() {
|
||||
return s_yuzu_input_device_get_name;
|
||||
return state.yuzu_input_device_get_name;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceGetGUID() {
|
||||
return s_yuzu_input_device_get_guid;
|
||||
return state.yuzu_input_device_get_guid;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceGetPort() {
|
||||
return s_yuzu_input_device_get_port;
|
||||
return state.yuzu_input_device_get_port;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
||||
return s_yuzu_input_device_get_supports_vibration;
|
||||
return state.yuzu_input_device_get_supports_vibration;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceVibrate() {
|
||||
return s_yuzu_input_device_vibrate;
|
||||
return state.yuzu_input_device_vibrate;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceGetAxes() {
|
||||
return s_yuzu_input_device_get_axes;
|
||||
return state.yuzu_input_device_get_axes;
|
||||
}
|
||||
|
||||
jmethodID GetYuzuDeviceHasKeys() {
|
||||
return s_yuzu_input_device_has_keys;
|
||||
return state.yuzu_input_device_has_keys;
|
||||
}
|
||||
|
||||
jmethodID GetAddNetPlayMessage() {
|
||||
return s_add_netplay_message;
|
||||
return state.add_netplay_message;
|
||||
}
|
||||
|
||||
jmethodID ClearChat() {
|
||||
return s_clear_chat;
|
||||
return state.clear_chat;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -436,20 +437,20 @@ namespace Common::Android {
|
||||
|
||||
// UnInitialize Android Storage
|
||||
Common::FS::Android::UnRegisterCallbacks();
|
||||
env->DeleteGlobalRef(s_native_library_class);
|
||||
env->DeleteGlobalRef(s_disk_cache_progress_class);
|
||||
env->DeleteGlobalRef(s_load_callback_stage_class);
|
||||
env->DeleteGlobalRef(s_game_dir_class);
|
||||
env->DeleteGlobalRef(s_game_class);
|
||||
env->DeleteGlobalRef(s_string_class);
|
||||
env->DeleteGlobalRef(s_pair_class);
|
||||
env->DeleteGlobalRef(s_overlay_control_data_class);
|
||||
env->DeleteGlobalRef(s_patch_class);
|
||||
env->DeleteGlobalRef(s_double_class);
|
||||
env->DeleteGlobalRef(s_integer_class);
|
||||
env->DeleteGlobalRef(s_boolean_class);
|
||||
env->DeleteGlobalRef(s_player_input_class);
|
||||
env->DeleteGlobalRef(s_yuzu_input_device_interface);
|
||||
env->DeleteGlobalRef(state.native_library_class);
|
||||
env->DeleteGlobalRef(state.disk_cache_progress_class);
|
||||
env->DeleteGlobalRef(state.load_callback_stage_class);
|
||||
env->DeleteGlobalRef(state.game_dir_class);
|
||||
env->DeleteGlobalRef(state.game_class);
|
||||
env->DeleteGlobalRef(state.string_class);
|
||||
env->DeleteGlobalRef(state.pair_class);
|
||||
env->DeleteGlobalRef(state.overlay_control_data_class);
|
||||
env->DeleteGlobalRef(state.patch_class);
|
||||
env->DeleteGlobalRef(state.double_class);
|
||||
env->DeleteGlobalRef(state.integer_class);
|
||||
env->DeleteGlobalRef(state.boolean_class);
|
||||
env->DeleteGlobalRef(state.player_input_class);
|
||||
env->DeleteGlobalRef(state.yuzu_input_device_interface);
|
||||
|
||||
// UnInitialize applets
|
||||
SoftwareKeyboard::CleanupJNI(env);
|
||||
@@ -463,7 +464,7 @@ namespace Common::Android {
|
||||
#endif
|
||||
|
||||
void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||
s_java_vm = vm;
|
||||
state.java_vm = vm;
|
||||
InitFFmpegOnLoad(vm);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
@@ -472,169 +473,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||
|
||||
// Initialize Java classes
|
||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||
|
||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
env->DeleteLocalRef(game_dir_class);
|
||||
|
||||
// Initialize methods
|
||||
s_exit_emulation_activity =
|
||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
||||
s_disk_cache_load_progress =
|
||||
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
||||
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
||||
state.exit_emulation_activity =
|
||||
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V");
|
||||
state.disk_cache_load_progress =
|
||||
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V");
|
||||
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
||||
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
|
||||
"(Ljava/lang/String;)Z");
|
||||
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
||||
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
s_on_emulation_started =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
||||
s_on_emulation_stopped =
|
||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
||||
s_on_program_changed =
|
||||
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
||||
state.on_emulation_started =
|
||||
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V");
|
||||
state.on_emulation_stopped =
|
||||
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V");
|
||||
state.on_program_changed =
|
||||
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V");
|
||||
|
||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
||||
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||
state.game_constructor = env->GetMethodID(game_class, "<init>",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||
env->DeleteLocalRef(game_class);
|
||||
|
||||
const jclass string_class = env->FindClass("java/lang/String");
|
||||
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||
env->DeleteLocalRef(string_class);
|
||||
|
||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||
s_pair_constructor =
|
||||
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||
state.pair_constructor =
|
||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||
env->DeleteLocalRef(pair_class);
|
||||
|
||||
const jclass overlay_control_data_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||
s_overlay_control_data_class =
|
||||
state.overlay_control_data_class =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||
s_overlay_control_data_constructor =
|
||||
state.overlay_control_data_constructor =
|
||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||
s_overlay_control_data_id_field =
|
||||
state.overlay_control_data_id_field =
|
||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||
s_overlay_control_data_enabled_field =
|
||||
state.overlay_control_data_enabled_field =
|
||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||
s_overlay_control_data_landscape_position_field =
|
||||
state.overlay_control_data_landscape_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_portrait_position_field =
|
||||
state.overlay_control_data_portrait_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_foldable_position_field =
|
||||
state.overlay_control_data_foldable_position_field =
|
||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||
s_overlay_control_data_individual_scale_field =
|
||||
state.overlay_control_data_individual_scale_field =
|
||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||
env->DeleteLocalRef(overlay_control_data_class);
|
||||
|
||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||
s_patch_constructor = env->GetMethodID(
|
||||
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||
state.patch_constructor = env->GetMethodID(
|
||||
patch_class, "<init>",
|
||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||
state.patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||
env->DeleteLocalRef(patch_class);
|
||||
|
||||
const jclass double_class = env->FindClass("java/lang/Double");
|
||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||
env->DeleteLocalRef(double_class);
|
||||
|
||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||
env->DeleteLocalRef(int_class);
|
||||
|
||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||
env->DeleteLocalRef(boolean_class);
|
||||
|
||||
const jclass player_input_class =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||
s_player_input_constructor = env->GetMethodID(
|
||||
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||
state.player_input_constructor = env->GetMethodID(
|
||||
player_input_class, "<init>",
|
||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||
s_player_input_buttons_field =
|
||||
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||
state.player_input_buttons_field =
|
||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||
s_player_input_analogs_field =
|
||||
state.player_input_analogs_field =
|
||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||
s_player_input_motions_field =
|
||||
state.player_input_motions_field =
|
||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||
s_player_input_vibration_enabled_field =
|
||||
state.player_input_vibration_enabled_field =
|
||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||
s_player_input_vibration_strength_field =
|
||||
state.player_input_vibration_strength_field =
|
||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||
s_player_input_body_color_left_field =
|
||||
state.player_input_body_color_left_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||
s_player_input_body_color_right_field =
|
||||
state.player_input_body_color_right_field =
|
||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||
s_player_input_button_color_left_field =
|
||||
state.player_input_button_color_left_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||
s_player_input_button_color_right_field =
|
||||
state.player_input_button_color_right_field =
|
||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||
s_player_input_profile_name_field =
|
||||
state.player_input_profile_name_field =
|
||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||
s_player_input_use_system_vibrator_field =
|
||||
state.player_input_use_system_vibrator_field =
|
||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||
env->DeleteLocalRef(player_input_class);
|
||||
|
||||
const jclass yuzu_input_device_interface =
|
||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||
s_yuzu_input_device_interface =
|
||||
state.yuzu_input_device_interface =
|
||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||
s_yuzu_input_device_get_name =
|
||||
state.yuzu_input_device_get_name =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_guid =
|
||||
state.yuzu_input_device_get_guid =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||
"()I");
|
||||
s_yuzu_input_device_get_supports_vibration =
|
||||
state.yuzu_input_device_get_supports_vibration =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||
"(F)V");
|
||||
s_yuzu_input_device_get_axes =
|
||||
state.yuzu_input_device_get_axes =
|
||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||
s_yuzu_input_device_has_keys =
|
||||
state.yuzu_input_device_has_keys =
|
||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
||||
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
|
||||
"(ILjava/lang/String;)V");
|
||||
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
||||
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
|
||||
|
||||
// Initialize Android Storage
|
||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
|
||||
|
||||
// Initialize applets
|
||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||
|
||||
+4
-18
@@ -22,36 +22,22 @@ template <typename T>
|
||||
return std::size_t(sizeof(T) * CHAR_BIT);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
|
||||
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
||||
return T(MostSignificantBit<T>(value));
|
||||
return std::bit_width(value) - 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
||||
const T log2_f = Log2Floor<T>(value);
|
||||
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] T NextPow2(T value) {
|
||||
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
|
||||
return std::bit_width(value - 1);
|
||||
}
|
||||
|
||||
template <size_t bit_index, typename T>
|
||||
requires std::is_integral_v<T>
|
||||
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
|
||||
[[nodiscard]] constexpr bool Bit(const T value) {
|
||||
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
|
||||
return ((value >> bit_index) & T(1)) == T(1);
|
||||
return (T(value >> bit_index) & T(1)) == T(1);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -25,26 +25,18 @@ template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
inline std::size_t HashValue(T val) {
|
||||
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
||||
const unsigned int length =
|
||||
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||
|
||||
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||
std::size_t seed = 0;
|
||||
|
||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
||||
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
||||
|
||||
return seed;
|
||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
|
||||
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
|
||||
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template <size_t Bits>
|
||||
struct HashCombineImpl {
|
||||
template <typename T>
|
||||
static inline T fn(T seed, T value) {
|
||||
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,14 +15,14 @@ namespace Common {
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||
return N((D(number) + divisor - 1) / divisor);
|
||||
}
|
||||
|
||||
/// Ceiled integer division with logarithmic divisor in base 2
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
namespace Common {
|
||||
|
||||
struct PairHash {
|
||||
template <class T1, class T2>
|
||||
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
||||
std::size_t seed = std::hash<T1>()(pair.first);
|
||||
boost::hash_combine(seed, std::hash<T2>()(pair.second));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IdentityHash {
|
||||
[[nodiscard]] size_t operator()(T value) const noexcept {
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+10
-16
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
@@ -16,14 +19,12 @@ namespace Common {
|
||||
|
||||
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
||||
if (c >= 65 && c <= 70) {
|
||||
return static_cast<u8>(c - 55);
|
||||
return u8(c - 55);
|
||||
}
|
||||
|
||||
if (c >= 97 && c <= 102) {
|
||||
return static_cast<u8>(c - 87);
|
||||
return u8(c - 87);
|
||||
}
|
||||
|
||||
return static_cast<u8>(c - 48);
|
||||
return u8(c - 48);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||
@@ -31,35 +32,28 @@ namespace Common {
|
||||
template <std::size_t Size, bool le = false>
|
||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||
|
||||
std::array<u8, Size> out{};
|
||||
if constexpr (le) {
|
||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||
}
|
||||
} else {
|
||||
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename ContiguousContainer>
|
||||
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
|
||||
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
||||
"Underlying type within the contiguous container must be u8.");
|
||||
|
||||
constexpr std::size_t pad_width = 2;
|
||||
|
||||
std::string out;
|
||||
out.reserve(std::size(data) * pad_width);
|
||||
|
||||
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
||||
for (const u8 c : data) {
|
||||
for (const u8 c : data)
|
||||
out += fmt::format(format_str, c);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -10,6 +10,7 @@
|
||||
#include <cstdlib>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
@@ -95,9 +96,10 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
||||
|
||||
template <typename It>
|
||||
Level GetLevelByName(const It begin, const It end) {
|
||||
std::string_view const sv{begin, end};
|
||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||
const char* level_name = GetLevelName(Level(i));
|
||||
if (Common::ComparePartialString(begin, end, level_name))
|
||||
auto const level_name = GetLevelName(Level(i));
|
||||
if (boost::iequals(sv, level_name))
|
||||
return Level(i);
|
||||
}
|
||||
return Level::Count;
|
||||
@@ -105,9 +107,10 @@ Level GetLevelByName(const It begin, const It end) {
|
||||
|
||||
template <typename It>
|
||||
Class GetClassByName(const It begin, const It end) {
|
||||
std::string_view const sv{begin, end};
|
||||
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
||||
const char* level_name = GetLogClassName(Class(i));
|
||||
if (Common::ComparePartialString(begin, end, level_name))
|
||||
auto const level_name = GetLogClassName(Class(i));
|
||||
if (boost::iequals(sv, level_name))
|
||||
return Class(i);
|
||||
}
|
||||
return Class::Count;
|
||||
@@ -120,12 +123,13 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
||||
return false;
|
||||
}
|
||||
const Level level = GetLevelByName(level_separator + 1, end);
|
||||
auto const sv = std::string_view{begin, level_separator};
|
||||
auto const level = GetLevelByName(level_separator + 1, end);
|
||||
if (level == Level::Count) {
|
||||
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
||||
return false;
|
||||
}
|
||||
if (Common::ComparePartialString(begin, level_separator, "*")) {
|
||||
if (boost::iequals(sv, "*")) {
|
||||
instance.class_levels.fill(level);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/param_package.h"
|
||||
@@ -15,17 +18,16 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr char KEY_VALUE_SEPARATOR = ':';
|
||||
constexpr char PARAM_SEPARATOR = ',';
|
||||
|
||||
constexpr char ESCAPE_CHARACTER = '$';
|
||||
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
|
||||
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
|
||||
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
|
||||
constexpr auto KEY_VALUE_SEPARATOR = ":";
|
||||
constexpr auto PARAM_SEPARATOR = ",";
|
||||
constexpr auto ESCAPE_CHARACTER = "$";
|
||||
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0";
|
||||
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1";
|
||||
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2";
|
||||
|
||||
/// A placeholder for empty param packages to avoid empty strings
|
||||
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
||||
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
|
||||
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
|
||||
|
||||
ParamPackage::ParamPackage(const std::string& serialized) {
|
||||
if (serialized == EMPTY_PLACEHOLDER) {
|
||||
@@ -33,20 +35,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
|
||||
}
|
||||
|
||||
std::vector<std::string> pairs;
|
||||
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
|
||||
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
|
||||
|
||||
for (const std::string& pair : pairs) {
|
||||
std::vector<std::string> key_value;
|
||||
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
|
||||
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
|
||||
if (key_value.size() != 2) {
|
||||
LOG_ERROR(Common, "invalid key pair {}", pair);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (std::string& part : key_value) {
|
||||
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
|
||||
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
|
||||
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
|
||||
for (auto& part : key_value) {
|
||||
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR);
|
||||
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR);
|
||||
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER);
|
||||
}
|
||||
|
||||
Set(key_value[0], std::move(key_value[1]));
|
||||
@@ -63,10 +65,10 @@ std::string ParamPackage::Serialize() const {
|
||||
|
||||
for (const auto& pair : data) {
|
||||
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
||||
for (std::string& part : key_value) {
|
||||
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
|
||||
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
|
||||
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
|
||||
for (auto& part : key_value) {
|
||||
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE);
|
||||
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE);
|
||||
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE);
|
||||
}
|
||||
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
||||
}
|
||||
|
||||
@@ -178,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||
}
|
||||
|
||||
bool IsGPUFenceBehaviorStrict() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
||||
}
|
||||
|
||||
bool IsFastmemEnabled() {
|
||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||
return bool(values.cpuopt_fastmem);
|
||||
|
||||
@@ -525,7 +525,7 @@ struct Values {
|
||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Strict,
|
||||
GpuFenceBehavior::Accurate,
|
||||
"gpu_fence_behavior",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
@@ -655,13 +655,6 @@ struct Values {
|
||||
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
@@ -979,7 +972,6 @@ bool IsDMALevelSafe();
|
||||
bool IsGPUFenceBehaviorDefault();
|
||||
bool IsGPUFenceBehaviorBalanced();
|
||||
bool IsGPUFenceBehaviorAccurate();
|
||||
bool IsGPUFenceBehaviorStrict();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
|
||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||
ENUM(GpuAccuracy, Low, High);
|
||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
||||
ENUM(CpuBackend, Dynarmic, Nce);
|
||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||
|
||||
@@ -6,16 +6,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <algorithm>
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _WIN32
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
@@ -26,60 +20,58 @@
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
static std::mutex vector_regions_mutex {};
|
||||
|
||||
struct VectorRegion {
|
||||
u64 start_page;
|
||||
u64 end_page;
|
||||
};
|
||||
|
||||
static std::mutex& GetVectorRegionsMutex() {
|
||||
static std::mutex* m = new std::mutex();
|
||||
return *m;
|
||||
}
|
||||
|
||||
static std::vector<VectorRegion>& GetVectorRegions() {
|
||||
static std::vector<VectorRegion>* v = new std::vector<VectorRegion>();
|
||||
return *v;
|
||||
}
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
const u64 fault_addr = info->ExceptionRecord->ExceptionInformation[1];
|
||||
const u64 access_type = info->ExceptionRecord->ExceptionInformation[0];
|
||||
const bool is_write = (access_type == 1);
|
||||
const u64 fault_page = fault_addr >> HostPageBits;
|
||||
|
||||
u64 addr = 0;
|
||||
u64 addr2 = 0;
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
for (const auto& region : GetVectorRegions()) {
|
||||
if (fault_page >= region.start_page && fault_page < region.end_page) {
|
||||
addr = fault_page;
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
const u64 page2 = (fault_addr + 0x3F) >> HostPageBits;
|
||||
if (page2 != fault_page && page2 >= region.start_page && page2 < region.end_page) {
|
||||
addr2 = page2;
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
if (addr != 0 || addr2 != 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", fault_addr);
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
|
||||
if (addr != 0 && !CommitVectorPage(addr << HostPageBits, is_write)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
if (addr2 != 0 && !CommitVectorPage(addr2 << HostPageBits, is_write)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
@@ -87,31 +79,31 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
const auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
const DWORD perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State == MEM_COMMIT) {
|
||||
DWORD old_protect {};
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||
LOG_ERROR(HW_Memory, "VirtualProtect failed at {:#x}, error {}", addr, GetLastError());
|
||||
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} (state {:#x})", addr, info.State);
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm) == nullptr) {
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
@@ -123,103 +115,60 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
|
||||
void DecommitVectorPage(uintptr_t base) noexcept {
|
||||
#if defined(_WIN32)
|
||||
if (!VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT)) {
|
||||
LOG_WARNING(HW_Memory, "VirtualFree(MEM_DECOMMIT) failed at {:#x}, error {}", base, GetLastError());
|
||||
}
|
||||
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
||||
#elif defined(__linux__)
|
||||
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED) != 0) {
|
||||
LOG_WARNING(HW_Memory, "madvise(MADV_DONTNEED) failed at {:#x}: {}", base, std::strerror(errno));
|
||||
}
|
||||
// Linux's MADV_DONTNEED zeros out pages for us
|
||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
||||
#else
|
||||
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE) != 0) {
|
||||
LOG_WARNING(HW_Memory, "madvise(MADV_FREE) failed at {:#x}: {}", base, std::strerror(errno));
|
||||
}
|
||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
||||
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto page = HostPageSize;
|
||||
if (size % page != 0) {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
if (size > SIZE_MAX - (page - 1)) {
|
||||
LOG_CRITICAL(HW_Memory, "Size {:#x} would overflow page alignment", size);
|
||||
return nullptr;
|
||||
}
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
GetVectorRegions().push_back({
|
||||
reinterpret_cast<u64>(base) >> HostPageBits,
|
||||
(reinterpret_cast<u64>(base) + size) >> HostPageBits,
|
||||
});
|
||||
}
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
int flags = MAP_ANON | MAP_PRIVATE;
|
||||
#ifdef MAP_NORESERVE
|
||||
flags |= MAP_NORESERVE;
|
||||
#endif
|
||||
#if defined(MAP_NOCORE)
|
||||
flags |= MAP_NOCORE;
|
||||
#endif
|
||||
void* base = mmap(nullptr, size, PROT_READ, flags, -1, 0);
|
||||
if (base == MAP_FAILED) {
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
}
|
||||
#ifdef MADV_HUGEPAGE
|
||||
if (base != nullptr) {
|
||||
madvise(base, size, MADV_HUGEPAGE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
#endif
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, std::strerror(errno));
|
||||
#endif
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (base == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto page = HostPageSize; size % page != 0) {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
{
|
||||
std::lock_guard lock(GetVectorRegionsMutex());
|
||||
auto& regions = GetVectorRegions();
|
||||
const u64 base_page = reinterpret_cast<u64>(base) >> HostPageBits;
|
||||
regions.erase(std::remove_if(regions.begin(), regions.end(),
|
||||
[base_page](const VectorRegion& r) { return r.start_page == base_page; }), regions.end());
|
||||
}
|
||||
if (!VirtualFree(base, 0, MEM_RELEASE)) {
|
||||
LOG_ERROR(HW_Memory, "VirtualFree failed, error {}", GetLastError());
|
||||
}
|
||||
std::lock_guard lock(vector_regions_mutex);
|
||||
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
if (munmap(base, size) != 0) {
|
||||
LOG_ERROR(HW_Memory, "munmap failed: {}", std::strerror(errno));
|
||||
}
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
|
||||
+106
-139
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
@@ -7,14 +7,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
@@ -32,7 +28,7 @@ constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
inline const u64 HostPageSize = static_cast<u64>(sysconf(_SC_PAGESIZE));
|
||||
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
@@ -47,18 +43,20 @@ template <typename T>
|
||||
// requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
SparseLargeVector() = default;
|
||||
constexpr SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept {
|
||||
if (count > SIZE_MAX / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector size overflow: {} elements", count);
|
||||
return;
|
||||
}
|
||||
Allocate(count * sizeof(T));
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
Release();
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
@@ -67,181 +65,150 @@ public:
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (count > SIZE_MAX / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector resize overflow: {} elements", count);
|
||||
return;
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
const std::size_t new_size = count * sizeof(T);
|
||||
if (new_size == alloc_size) {
|
||||
ZeroRegion(0, alloc_size / sizeof(T));
|
||||
return;
|
||||
}
|
||||
Release();
|
||||
Allocate(new_size);
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector RW access out of bounds @ {} (size {})", index, size());
|
||||
std::abort();
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed @ {} (offset {:#x})", index, byte_offset);
|
||||
std::abort();
|
||||
|
||||
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
||||
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
const T& GetOrDefault(std::size_t index) const noexcept {
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector RO access out of bounds @ {}", index);
|
||||
return DefaultValue();
|
||||
}
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
#ifdef _WIN32
|
||||
if (!IsPageCommitted(static_cast<u64>(index) * sizeof(T))) {
|
||||
return DefaultValue();
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector write out of bounds @ {}", index);
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed for write @ {}", index);
|
||||
return;
|
||||
}
|
||||
base_ptr[index] = value;
|
||||
if (IsCommittedPage(index) || CommitPage(index))
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
if (base_ptr == nullptr || start >= end_) return;
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
|
||||
const u64 start_off = static_cast<u64>(start) * sizeof(T);
|
||||
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||
const u64 first_page_end = (start_off + HostPageSize - 1) & HostPageMask;
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
|
||||
if (start_off < first_page_end) {
|
||||
const u64 chunk_end = (std::min)(first_page_end, end_off);
|
||||
const u64 chunk_size = chunk_end - start_off;
|
||||
if (chunk_size != 0 && IsPageCommitted(start_off)) {
|
||||
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + start_off), 0, chunk_size);
|
||||
}
|
||||
if (end_off <= first_page_end) return;
|
||||
if (IsCommittedPage(start)) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
}
|
||||
|
||||
for (u64 off = first_page_end; off < end_off; off += HostPageSize) {
|
||||
if (!IsPageCommitted(off)) continue;
|
||||
const u64 remaining = end_off - off;
|
||||
if (remaining >= HostPageSize) {
|
||||
DecommitPage(off);
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
||||
if (!IsCommittedPage(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (end - page >= HostPageSize) {
|
||||
DecommitPage(index);
|
||||
} else {
|
||||
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + off), 0, remaining);
|
||||
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommitRegion(std::size_t index, std::size_t end_) noexcept {
|
||||
if (base_ptr == nullptr || index >= end_) return;
|
||||
const u64 start_off = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||
const u64 start_page = start_off & HostPageMask;
|
||||
for (u64 off = start_page; off < end_off; off += HostPageSize) {
|
||||
if (!IsPageCommitted(off)) {
|
||||
(void)CommitPage(off);
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T& GetUnchecked(std::size_t index) noexcept { return base_ptr[index]; }
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& operator[](std::size_t index) const noexcept { return GetOrDefault(index); }
|
||||
[[nodiscard]] const T* data() const noexcept { return base_ptr; }
|
||||
[[nodiscard]] std::size_t size() const noexcept { return alloc_size / sizeof(T); }
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
void Allocate(std::size_t new_size) noexcept {
|
||||
alloc_size = new_size;
|
||||
if (alloc_size == 0) {
|
||||
base_ptr = nullptr;
|
||||
committed_pages.reset();
|
||||
return;
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
return false;
|
||||
}
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
const std::size_t num_pages = NumPages();
|
||||
const std::size_t num_words = (num_pages + 63) / 64;
|
||||
committed_pages = std::make_unique<std::atomic<u64>[]>(num_words);
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
void Release() noexcept {
|
||||
if (base_ptr != nullptr) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
base_ptr = nullptr;
|
||||
}
|
||||
committed_pages.reset();
|
||||
alloc_size = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 NumPages() const noexcept {
|
||||
return (alloc_size + HostPageSize - 1) >> HostPageBits;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPageCommitted(u64 byte_offset) const noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
if (committed_pages == nullptr || page_index >= NumPages()) return false;
|
||||
const auto val = committed_pages[page_index >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page_index & 63)) & 1;
|
||||
}
|
||||
|
||||
void SetPageBit(u64 page_index, bool value) noexcept {
|
||||
if (committed_pages == nullptr) return;
|
||||
const u64 bit = 1ULL << (page_index & 63);
|
||||
auto& atom = committed_pages[page_index >> 6];
|
||||
if (value) {
|
||||
atom.fetch_or(bit, std::memory_order_release);
|
||||
} else {
|
||||
atom.fetch_and(~bit, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
bool CommitPage(u64 byte_offset) noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||
|
||||
if (IsPageCommitted(byte_offset)) return true;
|
||||
|
||||
constexpr bool CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
if (!CommitVectorPage(page_addr, true)) return false;
|
||||
if (!CommitVectorPage(page, true)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (mprotect(reinterpret_cast<void*>(page_addr), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||
LOG_ERROR(Common_Memory, "mprotect failed at {:#x}: {}", page_addr, std::strerror(errno));
|
||||
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
SetPageBit(page_index, true);
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DecommitPage(u64 byte_offset) noexcept {
|
||||
const u64 page_index = byte_offset >> HostPageBits;
|
||||
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||
DecommitVectorPage(page_addr);
|
||||
SetPageBit(page_index, false);
|
||||
}
|
||||
constexpr void DecommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
|
||||
[[nodiscard]] const T& DefaultValue() const noexcept {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
||||
DecommitVectorPage(page);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
std::unique_ptr<std::atomic<u64>[]> committed_pages{};
|
||||
alignas(T) const std::array<u8, sizeof(T)> default_val{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
|
||||
@@ -24,22 +24,6 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Make a string lowercase
|
||||
std::string ToLower(const std::string_view sv) {
|
||||
std::string str{sv};
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](auto const c) { return char(std::tolower(c)); });
|
||||
return str;
|
||||
}
|
||||
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(const std::string_view sv) {
|
||||
std::string str{sv};
|
||||
std::transform(str.begin(), str.end(), str.begin(),
|
||||
[](auto const c) { return char(std::toupper(c)); });
|
||||
return str;
|
||||
}
|
||||
|
||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||
std::string* _pExtension) {
|
||||
if (full_path.empty())
|
||||
@@ -80,41 +64,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
return true;
|
||||
}
|
||||
|
||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
|
||||
std::istringstream iss(str);
|
||||
output.resize(1);
|
||||
|
||||
while (std::getline(iss, *output.rbegin(), delim)) {
|
||||
output.emplace_back();
|
||||
}
|
||||
|
||||
output.pop_back();
|
||||
}
|
||||
|
||||
std::string TabsToSpaces(int tab_size, std::string in) {
|
||||
std::size_t i = 0;
|
||||
|
||||
while ((i = in.find('\t')) != std::string::npos) {
|
||||
in.replace(i, 1, tab_size, ' ');
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
|
||||
std::size_t pos = 0;
|
||||
|
||||
if (src == dest)
|
||||
return result;
|
||||
|
||||
while ((pos = result.find(src, pos)) != std::string::npos) {
|
||||
result.replace(pos, src.size(), dest);
|
||||
pos += dest.length();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(std::u16string_view input) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(input.data(), input.data() + input.size());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
@@ -16,12 +16,6 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Make a string lowercase
|
||||
[[nodiscard]] std::string ToLower(const std::string_view sv);
|
||||
|
||||
/// Make a string uppercase
|
||||
[[nodiscard]] std::string ToUpper(const std::string_view sv);
|
||||
|
||||
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
@@ -29,37 +23,8 @@ namespace Common {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
/// Turns " hej " into "hej". Also handles tabs.
|
||||
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
|
||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
||||
if (str.npos != s)
|
||||
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
|
||||
return {};
|
||||
}
|
||||
|
||||
/// "\"hello\"" is turned to "hello"
|
||||
/// This one assumes that the string has already been space stripped in both
|
||||
/// ends, as done by StripSpaces above, for example.
|
||||
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
|
||||
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
||||
return std::string{s.substr(1, s.size() - 2)};
|
||||
return std::string{s};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
|
||||
return value ? "True" : "False";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
||||
|
||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||
|
||||
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||
std::string* _pExtension);
|
||||
|
||||
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
|
||||
const std::string& dest);
|
||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
|
||||
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||
@@ -73,30 +38,13 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||
|
||||
/**
|
||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||
* `other` for equality.
|
||||
*/
|
||||
template <typename InIt>
|
||||
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
|
||||
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
||||
if (*begin != *other) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Only return true if both strings finished at the same point
|
||||
return (begin == end) == (*other == '\0');
|
||||
}
|
||||
|
||||
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||
/// NUL-terminated then the string ends at max_len characters.
|
||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
||||
std::size_t max_len);
|
||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len);
|
||||
|
||||
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||
/// max_len_bytes.
|
||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len);
|
||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/zbic_compression.h"
|
||||
|
||||
#define ZSTD_ZBIC_SUPPORT 1
|
||||
#define ZSTDLIB_VISIBLE static
|
||||
#define ZSTDLIB_HIDDEN static
|
||||
#define ZSTDERRORLIB_VISIBLE static
|
||||
#define ZSTDERRORLIB_HIDDEN static
|
||||
#undef ZSTD_MULTITHREAD
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#undef _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "zstd.h"
|
||||
#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol
|
||||
#include "zstd.c"
|
||||
#undef g_ZSTD_threading_useless_symbol
|
||||
|
||||
namespace Common::Compression {
|
||||
|
||||
bool IsZBIC(std::span<const u8> src) {
|
||||
if (src.size() < sizeof(u32)) {
|
||||
return false;
|
||||
}
|
||||
u32 magic = 0;
|
||||
std::memcpy(&magic, src.data(), sizeof(u32));
|
||||
return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC")
|
||||
}
|
||||
|
||||
int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src) {
|
||||
if (dst.empty() || src.empty()) {
|
||||
return -1;
|
||||
}
|
||||
const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size());
|
||||
if (ZSTD_isError(res)) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
|
||||
} // namespace Common::Compression
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Compression {
|
||||
|
||||
[[nodiscard]] bool IsZBIC(std::span<const u8> src);
|
||||
|
||||
[[nodiscard]] int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src);
|
||||
|
||||
} // namespace Common::Compression
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
+12
-19
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||
@@ -178,26 +178,19 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||
auto& data = core_data[core];
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT {
|
||||
data.host_context->Exit();
|
||||
};
|
||||
|
||||
// Running
|
||||
if (!gpu_barrier->Sync(token)) {
|
||||
return;
|
||||
gpu_barrier->arrive_and_wait();
|
||||
if (!stop_token.stop_requested()) {
|
||||
if (!is_async_gpu && !is_multicore) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
}
|
||||
|
||||
if (!is_async_gpu && !is_multicore) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
data.host_context->Exit();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <barrier>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -52,7 +53,7 @@ public:
|
||||
}
|
||||
|
||||
void OnGpuReady() {
|
||||
gpu_barrier->Sync();
|
||||
gpu_barrier->arrive_and_wait();
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
@@ -95,7 +96,7 @@ private:
|
||||
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
std::optional<Common::Barrier> gpu_barrier{};
|
||||
std::optional<std::barrier<>> gpu_barrier{};
|
||||
struct CoreData {
|
||||
std::shared_ptr<Common::Fiber> host_context;
|
||||
std::jthread host_thread;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
@@ -622,7 +623,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||
} else {
|
||||
out[0] = Common::ToLower(out[0]);
|
||||
boost::algorithm::to_lower(out[0]);
|
||||
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
||||
const auto& index = iter128->second;
|
||||
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -41,16 +42,13 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
|
||||
|
||||
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
||||
|
||||
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
||||
const std::string& name) {
|
||||
const auto upper = Common::ToUpper(name);
|
||||
|
||||
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) {
|
||||
const auto upper = boost::algorithm::to_upper_copy(name);
|
||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||
if (dir->GetFile(fname) != nullptr) {
|
||||
return dir->GetFile(fname);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -73,12 +74,10 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
|
||||
#else
|
||||
const auto subdirs = dir->GetSubdirectories();
|
||||
for (const auto& subdir : subdirs) {
|
||||
std::string dir_name = Common::ToLower(subdir->GetName());
|
||||
if (dir_name == name) {
|
||||
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
|
||||
return subdir;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <openssl/evp.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/path_util.h"
|
||||
@@ -1414,11 +1415,10 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
||||
|
||||
if (extension == "nsp") {
|
||||
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
|
||||
if (ext == "nsp") {
|
||||
ProcessNSP(file);
|
||||
} else if (extension == "xci") {
|
||||
} else if (ext == "xci") {
|
||||
ProcessXCI(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
@@ -63,8 +64,8 @@ NAX::NAX(VirtualFile file_)
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string two_dir = Common::ToUpper(std::string{match[1]});
|
||||
const std::string nca_id = Common::ToLower(std::string{match[2]});
|
||||
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]});
|
||||
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]});
|
||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,8 +22,14 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
|
||||
|
||||
// Virtual to Physical core map.
|
||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
||||
0, 1, 2, 3, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 3,
|
||||
};
|
||||
|
||||
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <string>
|
||||
#include <concepts>
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include "common/concepts.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging.h"
|
||||
@@ -170,8 +171,7 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
else if (name == "00")
|
||||
return FileType::NCA;
|
||||
|
||||
auto const extension =
|
||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||
if (extension == "nro")
|
||||
return FileType::NRO;
|
||||
else if (extension == "nso")
|
||||
|
||||
+31
-4
@@ -7,12 +7,14 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/lz4_compression.h"
|
||||
#include "common/zbic_compression.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
@@ -104,11 +106,36 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
||||
if (nso_header.IsSegmentCompressed(i)) {
|
||||
int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]);
|
||||
ASSERT(r == int(nso_header.segments[i].size));
|
||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size);
|
||||
if (nso_header.IsZBICCompressed()) {
|
||||
// ZBIC compression
|
||||
const int r = Common::Compression::DecompressDataZBIC(
|
||||
std::span<u8>{decompressed_size}.first(nso_header.segments[i].size),
|
||||
std::span<const u8>{compressed_data}.first(nso_header.segments_compressed_size[i])
|
||||
);
|
||||
ASSERT(r > 0);
|
||||
} else {
|
||||
// LZ4 compression
|
||||
int r = Common::Compression::DecompressDataLZ4(
|
||||
decompressed_size.data(),
|
||||
nso_header.segments[i].size,
|
||||
compressed_data.data(),
|
||||
nso_header.segments_compressed_size[i]
|
||||
);
|
||||
ASSERT(r == int(nso_header.segments[i].size));
|
||||
}
|
||||
|
||||
std::memcpy(
|
||||
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||
decompressed_size.data(),
|
||||
nso_header.segments[i].size
|
||||
);
|
||||
} else {
|
||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size);
|
||||
// Not compressed
|
||||
std::memcpy(
|
||||
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||
compressed_data.data(),
|
||||
nso_header.segments[i].size
|
||||
);
|
||||
}
|
||||
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
||||
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -58,6 +61,9 @@ struct NSOHeader {
|
||||
std::array<SHA256Hash, 3> segment_hashes;
|
||||
|
||||
bool IsSegmentCompressed(size_t segment_num) const;
|
||||
bool IsZBICCompressed() const {
|
||||
return ((flags >> 7) & 1) != 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
@@ -132,7 +133,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
|
||||
line = Common::StripSpaces(line);
|
||||
boost::trim(line);
|
||||
if (line.empty()) {
|
||||
// An empty line marks start of the IP ban list
|
||||
ban_list_type = true;
|
||||
|
||||
@@ -761,9 +761,6 @@ void EmulatedController::StartMotionCalibration() {
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
if (index >= controller.button_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -916,21 +913,10 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
if (npad_type == NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Handheld) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
}
|
||||
} else if (npad_type != NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Player1) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
} else if (player.connected && !controller_connected[player_index]) {
|
||||
Connect();
|
||||
controller_connected[player_index] = true;
|
||||
}
|
||||
}
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
if (player.connected) {
|
||||
Connect();
|
||||
}
|
||||
|
||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "hid_core/frontend/motion_input.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "hid_core/irsensor/irs_types.h"
|
||||
|
||||
@@ -585,7 +584,6 @@ private:
|
||||
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
||||
DEFAULT_VIBRATION_VALUE};
|
||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
||||
|
||||
// Atomically synched values
|
||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||
|
||||
@@ -226,9 +226,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting."));
|
||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||
tr("May reduce shader stutter."));
|
||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||
@@ -442,7 +440,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
||||
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
||||
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
||||
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
||||
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include "common/httplib.h"
|
||||
|
||||
@@ -45,7 +46,7 @@ void DiscordImpl::Pause() {
|
||||
|
||||
std::string DiscordImpl::GetGameString(const std::string& title) {
|
||||
// Convert to lowercase
|
||||
std::string icon_name = Common::ToLower(title);
|
||||
std::string icon_name = boost::algorithm::to_lower_copy(title);
|
||||
|
||||
// Replace spaces with dashes
|
||||
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
|
||||
|
||||
@@ -249,6 +249,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||
if (has_new_downloads) {
|
||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||
if (should_sync) {
|
||||
runtime.Finish();
|
||||
}
|
||||
}
|
||||
|
||||
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||
@@ -1230,7 +1234,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
||||
buffer.MarkUsage(offset, size);
|
||||
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true);
|
||||
}
|
||||
|
||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||
@@ -1516,10 +1520,12 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
if (needs_sync) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
}
|
||||
}
|
||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||
gpu_modified_ranges.Add(device_addr, size);
|
||||
@@ -1535,8 +1541,11 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
||||
const BufferId buffer_id = page_table[page];
|
||||
if (buffer_id) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
WaitForGpuFenceIfNeeded(buffer);
|
||||
if (buffer.IsInBounds(device_addr, size)) {
|
||||
const bool should_sync = Settings::IsGPUFenceBehaviorAccurate();
|
||||
if (should_sync) {
|
||||
SynchronizeBufferWrites(buffer);
|
||||
}
|
||||
bool usable = true;
|
||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
||||
@@ -1552,17 +1561,11 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
||||
void BufferCache<P>::SynchronizeBufferWrites(Buffer& buffer) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||
if (gpu_fence_accurate || gpu_fence_strict) {
|
||||
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
||||
const u64 buffer_tick = buffer.getWriteTick();
|
||||
const u64 gpu_tick = runtime.KnownGpuTick();
|
||||
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
||||
runtime.Wait(buffer_tick);
|
||||
}
|
||||
const u64 buffer_tick = buffer.getWriteTick();
|
||||
if (!runtime.IsFree(buffer_tick)) {
|
||||
runtime.Wait(buffer_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1795,9 +1798,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
if (immediate_buffer.empty()) {
|
||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||
}
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||
}
|
||||
@@ -1816,9 +1816,6 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
for (BufferCopy& copy : copies) {
|
||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||
// Apply the staging offset
|
||||
copy.src_offset += upload_staging.offset;
|
||||
|
||||
@@ -430,11 +430,11 @@ private:
|
||||
|
||||
void UpdateComputeTextureBuffers();
|
||||
|
||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false);
|
||||
|
||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
||||
|
||||
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||
void SynchronizeBufferWrites(Buffer& buffer);
|
||||
|
||||
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
void SignalFence(std::function<void()>&& func) {
|
||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict();
|
||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool should_flush = ShouldFlush();
|
||||
if constexpr (!can_async_check) {
|
||||
TryReleasePendingFences<false>();
|
||||
|
||||
@@ -260,7 +260,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
||||
};
|
||||
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
||||
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence;
|
||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence;
|
||||
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
||||
pointer, pointer_timestamp] {
|
||||
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -55,7 +55,7 @@ size_t StagingBuffers::RequestBuffer(size_t requested_size) {
|
||||
}
|
||||
StagingBufferAlloc alloc;
|
||||
alloc.buffer.Create();
|
||||
const auto next_pow2_size = Common::NextPow2(requested_size);
|
||||
const auto next_pow2_size = std::bit_ceil(requested_size);
|
||||
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
|
||||
storage_flags | GL_MAP_PERSISTENT_BIT);
|
||||
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
|
||||
|
||||
@@ -1404,7 +1404,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
|
||||
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
|
||||
if (pbo_size < copy_size) {
|
||||
intermediate_pbo.Create();
|
||||
pbo_size = Common::NextPow2(copy_size);
|
||||
pbo_size = std::bit_ceil(copy_size);
|
||||
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
|
||||
}
|
||||
// Copy from source to PBO
|
||||
|
||||
@@ -419,15 +419,15 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
|
||||
}
|
||||
|
||||
u64 BufferCacheRuntime::CurrentTick() {
|
||||
return scheduler.GetMasterSemaphore().CurrentTick();
|
||||
return scheduler.CurrentTick();
|
||||
}
|
||||
|
||||
u64 BufferCacheRuntime::KnownGpuTick() {
|
||||
return scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||
bool BufferCacheRuntime::IsFree(u64 tick) {
|
||||
return scheduler.IsFree(tick);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::Wait(u64 buffer_tick) {
|
||||
scheduler.Wait(buffer_tick);
|
||||
void BufferCacheRuntime::Wait(u64 tick) {
|
||||
scheduler.Wait(tick);
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::Finish() {
|
||||
|
||||
@@ -112,9 +112,9 @@ public:
|
||||
|
||||
u64 CurrentTick();
|
||||
|
||||
u64 KnownGpuTick();
|
||||
bool IsFree(u64 tick);
|
||||
|
||||
void Wait(u64 buffer_tick);
|
||||
void Wait(u64 tick);
|
||||
|
||||
void Finish();
|
||||
|
||||
|
||||
@@ -1011,7 +1011,7 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
|
||||
if (buffers[level]) {
|
||||
return *buffers[level];
|
||||
}
|
||||
const auto new_size = Common::NextPow2(needed_size);
|
||||
const auto new_size = std::bit_ceil(needed_size);
|
||||
static constexpr VkBufferUsageFlags flags =
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||
|
||||
@@ -2190,7 +2190,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
|
||||
image.flags &= ~ImageFlagBits::BadOverlap;
|
||||
lru_cache.Free(image.lru_index);
|
||||
const auto& clear_page_table =
|
||||
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& selected_page_table) {
|
||||
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>>& selected_page_table) {
|
||||
const auto page_it = selected_page_table.find(page);
|
||||
if (page_it == selected_page_table.end()) {
|
||||
ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <queue>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/lru_cache.h"
|
||||
#include <ranges>
|
||||
@@ -69,7 +68,7 @@ struct AsyncDecodeContext {
|
||||
std::atomic_bool complete;
|
||||
};
|
||||
|
||||
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
|
||||
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>>;
|
||||
|
||||
class TextureCacheChannelInfo : public ChannelInfo {
|
||||
public:
|
||||
@@ -443,7 +442,7 @@ private:
|
||||
u64 last_framebuffer_serial = 0;
|
||||
|
||||
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
|
||||
::Common::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
|
||||
::Common::unordered_map<u64, std::vector<ImageMapId>> page_table;
|
||||
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
|
||||
|
||||
DAddr virtual_invalid_space{};
|
||||
|
||||
Reference in New Issue
Block a user