Compare commits

..

12 Commits

Author SHA1 Message Date
MaranBr 7c49b70107 Minor improvement 2026-10-01 10:35:37 -04:00
MaranBr 3f41a04732 Ops 2026-10-01 09:26:42 -04:00
MaranBr 76fa7445d0 Remove Legacy Rescale Pass hack 2026-10-01 09:13:22 -04:00
MaranBr c48b6bccee Fix headers 2026-10-01 09:08:07 -04:00
MaranBr bf3b6886a0 Skip unsupported fragment shader pattern on non-NVIDIA drivers 2026-10-01 09:04:40 -04:00
lizzie d3550c4571 [qt] Filter collates accents (Unicode NFD) (#4380)
Now "POKEMON" and "pokemon" are the same, and search's no longer case sensitive.
Maybe we could transliterate? But that would require icu which is a bit overkill.
Main point is to be able to search "Pokemon" (accent on 'e') without actually typing the accent

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

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

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

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

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

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

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

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

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

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

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

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

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

Imma try to be short:

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

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

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

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

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

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

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

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

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4498
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-30 00:57:23 +02:00
74 changed files with 12523 additions and 12783 deletions
+1 -1
View File
@@ -79,7 +79,7 @@
"name": "ffmpeg",
"package": "FFmpeg",
"repo": "crueter-ci/FFmpeg",
"version": "9.0.1-1788120736-bf1b838f2a"
"version": "9.0.2-1790726865-946fcce07b"
},
"fmt": {
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
+455 -473
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+440 -463
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+440 -463
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+440 -463
View File
File diff suppressed because it is too large Load Diff
+440 -463
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
+440 -463
View File
File diff suppressed because it is too large Load Diff
+439 -456
View File
File diff suppressed because it is too large Load Diff
@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File
@@ -146,11 +147,11 @@ class DriverViewModel : ViewModel() {
private fun wipeGameShaders(game: Game) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val externalFilesDir = YuzuApplication.appContext.getExternalFilesDir(null)
val userDirectory = DirectoryInitialization.userDirectory
?: return@withContext
val shaderDir = File(
externalFilesDir.absolutePath +
"/shader/" + game.settingsName.lowercase()
userDirectory +
"/cache/shader/" + game.settingsName.lowercase()
)
if (shaderDir.exists()) {
shaderDir.deleteRecursively()
+1
View File
@@ -62,6 +62,7 @@ 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
+251 -252
View File
@@ -12,94 +12,93 @@
#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;
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 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 string_class;
jclass pair_class;
jmethodID pair_constructor;
jfieldID pair_first_field;
jfieldID pair_second_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 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_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 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_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 double_class;
jmethodID double_constructor;
jmethodID double_value_method;
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 integer_class;
jmethodID integer_constructor;
jmethodID integer_value_method;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jmethodID s_double_value_method;
jclass boolean_class;
jmethodID boolean_constructor;
jmethodID boolean_value_method;
static jclass s_integer_class;
static jmethodID s_integer_constructor;
static jmethodID s_integer_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_boolean_class;
static jmethodID s_boolean_constructor;
static jmethodID s_boolean_value_method;
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_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;
jmethodID add_netplay_message;
jmethodID clear_chat;
} state;
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;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
@@ -107,14 +106,14 @@ namespace Common::Android {
JNIEnv *GetEnvForThread() {
thread_local static struct OwnedEnv {
OwnedEnv() {
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED)
state.java_vm->AttachCurrentThread(&env, nullptr);
s_java_vm->AttachCurrentThread(&env, nullptr);
}
~OwnedEnv() {
if (status == JNI_EDETACHED)
state.java_vm->DetachCurrentThread();
s_java_vm->DetachCurrentThread();
}
int status;
@@ -124,303 +123,303 @@ namespace Common::Android {
}
jclass GetNativeLibraryClass() {
return state.native_library_class;
return s_native_library_class;
}
jclass GetDiskCacheProgressClass() {
return state.disk_cache_progress_class;
return s_disk_cache_progress_class;
}
jclass GetDiskCacheLoadCallbackStageClass() {
return state.load_callback_stage_class;
return s_load_callback_stage_class;
}
jclass GetGameDirClass() {
return state.game_dir_class;
return s_game_dir_class;
}
jmethodID GetGameDirConstructor() {
return state.game_dir_constructor;
return s_game_dir_constructor;
}
jmethodID GetExitEmulationActivity() {
return state.exit_emulation_activity;
return s_exit_emulation_activity;
}
jmethodID GetDiskCacheLoadProgress() {
return state.disk_cache_load_progress;
return s_disk_cache_load_progress;
}
jmethodID GetCopyToStorage() {
return state.copy_to_storage;
return s_copy_to_storage;
}
jmethodID GetFileExists() {
return state.file_exists;
return s_file_exists;
}
jmethodID GetFileExtension() {
return state.file_extension;
return s_file_extension;
}
jmethodID GetOnEmulationStarted() {
return state.on_emulation_started;
return s_on_emulation_started;
}
jmethodID GetOnEmulationStopped() {
return state.on_emulation_stopped;
return s_on_emulation_stopped;
}
jmethodID GetOnProgramChanged() {
return state.on_program_changed;
return s_on_program_changed;
}
jclass GetGameClass() {
return state.game_class;
return s_game_class;
}
jmethodID GetGameConstructor() {
return state.game_constructor;
return s_game_constructor;
}
jfieldID GetGameTitleField() {
return state.game_title_field;
return s_game_title_field;
}
jfieldID GetGamePathField() {
return state.game_path_field;
return s_game_path_field;
}
jfieldID GetGameProgramIdField() {
return state.game_program_id_field;
return s_game_program_id_field;
}
jfieldID GetGameDeveloperField() {
return state.game_developer_field;
return s_game_developer_field;
}
jfieldID GetGameVersionField() {
return state.game_version_field;
return s_game_version_field;
}
jfieldID GetGameIsHomebrewField() {
return state.game_is_homebrew_field;
return s_game_is_homebrew_field;
}
jclass GetStringClass() {
return state.string_class;
return s_string_class;
}
jclass GetPairClass() {
return state.pair_class;
return s_pair_class;
}
jmethodID GetPairConstructor() {
return state.pair_constructor;
return s_pair_constructor;
}
jfieldID GetPairFirstField() {
return state.pair_first_field;
return s_pair_first_field;
}
jfieldID GetPairSecondField() {
return state.pair_second_field;
return s_pair_second_field;
}
jclass GetOverlayControlDataClass() {
return state.overlay_control_data_class;
return s_overlay_control_data_class;
}
jmethodID GetOverlayControlDataConstructor() {
return state.overlay_control_data_constructor;
return s_overlay_control_data_constructor;
}
jfieldID GetOverlayControlDataIdField() {
return state.overlay_control_data_id_field;
return s_overlay_control_data_id_field;
}
jfieldID GetOverlayControlDataEnabledField() {
return state.overlay_control_data_enabled_field;
return s_overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataIndividualScaleField() {
return state.overlay_control_data_individual_scale_field;
return s_overlay_control_data_individual_scale_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return state.overlay_control_data_landscape_position_field;
return s_overlay_control_data_landscape_position_field;
}
jfieldID GetOverlayControlDataPortraitPositionField() {
return state.overlay_control_data_portrait_position_field;
return s_overlay_control_data_portrait_position_field;
}
jfieldID GetOverlayControlDataFoldablePositionField() {
return state.overlay_control_data_foldable_position_field;
return s_overlay_control_data_foldable_position_field;
}
jclass GetPatchClass() {
return state.patch_class;
return s_patch_class;
}
jmethodID GetPatchConstructor() {
return state.patch_constructor;
return s_patch_constructor;
}
jfieldID GetPatchEnabledField() {
return state.patch_enabled_field;
return s_patch_enabled_field;
}
jfieldID GetPatchNameField() {
return state.patch_name_field;
return s_patch_name_field;
}
jfieldID GetPatchVersionField() {
return state.patch_version_field;
return s_patch_version_field;
}
jfieldID GetPatchTypeField() {
return state.patch_type_field;
return s_patch_type_field;
}
jfieldID GetPatchProgramIdField() {
return state.patch_program_id_field;
return s_patch_program_id_field;
}
jfieldID GetPatchTitleIdField() {
return state.patch_title_id_field;
return s_patch_title_id_field;
}
jclass GetDoubleClass() {
return state.double_class;
return s_double_class;
}
jmethodID GetDoubleConstructor() {
return state.double_constructor;
return s_double_constructor;
}
jmethodID GetDoubleValueMethod() {
return state.double_value_method;
return s_double_value_method;
}
jclass GetIntegerClass() {
return state.integer_class;
return s_integer_class;
}
jmethodID GetIntegerConstructor() {
return state.integer_constructor;
return s_integer_constructor;
}
jmethodID GetIntegerValueMethod() {
return state.integer_value_method;
return s_integer_value_method;
}
jclass GetBooleanClass() {
return state.boolean_class;
return s_boolean_class;
}
jmethodID GetBooleanConstructor() {
return state.boolean_constructor;
return s_boolean_constructor;
}
jmethodID GetBooleanValueMethod() {
return state.boolean_value_method;
return s_boolean_value_method;
}
jclass GetPlayerInputClass() {
return state.player_input_class;
return s_player_input_class;
}
jmethodID GetPlayerInputConstructor() {
return state.player_input_constructor;
return s_player_input_constructor;
}
jfieldID GetPlayerInputConnectedField() {
return state.player_input_connected_field;
return s_player_input_connected_field;
}
jfieldID GetPlayerInputButtonsField() {
return state.player_input_buttons_field;
return s_player_input_buttons_field;
}
jfieldID GetPlayerInputAnalogsField() {
return state.player_input_analogs_field;
return s_player_input_analogs_field;
}
jfieldID GetPlayerInputMotionsField() {
return state.player_input_motions_field;
return s_player_input_motions_field;
}
jfieldID GetPlayerInputVibrationEnabledField() {
return state.player_input_vibration_enabled_field;
return s_player_input_vibration_enabled_field;
}
jfieldID GetPlayerInputVibrationStrengthField() {
return state.player_input_vibration_strength_field;
return s_player_input_vibration_strength_field;
}
jfieldID GetPlayerInputBodyColorLeftField() {
return state.player_input_body_color_left_field;
return s_player_input_body_color_left_field;
}
jfieldID GetPlayerInputBodyColorRightField() {
return state.player_input_body_color_right_field;
return s_player_input_body_color_right_field;
}
jfieldID GetPlayerInputButtonColorLeftField() {
return state.player_input_button_color_left_field;
return s_player_input_button_color_left_field;
}
jfieldID GetPlayerInputButtonColorRightField() {
return state.player_input_button_color_right_field;
return s_player_input_button_color_right_field;
}
jfieldID GetPlayerInputProfileNameField() {
return state.player_input_profile_name_field;
return s_player_input_profile_name_field;
}
jfieldID GetPlayerInputUseSystemVibratorField() {
return state.player_input_use_system_vibrator_field;
return s_player_input_use_system_vibrator_field;
}
jclass GetYuzuInputDeviceInterface() {
return state.yuzu_input_device_interface;
return s_yuzu_input_device_interface;
}
jmethodID GetYuzuDeviceGetName() {
return state.yuzu_input_device_get_name;
return s_yuzu_input_device_get_name;
}
jmethodID GetYuzuDeviceGetGUID() {
return state.yuzu_input_device_get_guid;
return s_yuzu_input_device_get_guid;
}
jmethodID GetYuzuDeviceGetPort() {
return state.yuzu_input_device_get_port;
return s_yuzu_input_device_get_port;
}
jmethodID GetYuzuDeviceGetSupportsVibration() {
return state.yuzu_input_device_get_supports_vibration;
return s_yuzu_input_device_get_supports_vibration;
}
jmethodID GetYuzuDeviceVibrate() {
return state.yuzu_input_device_vibrate;
return s_yuzu_input_device_vibrate;
}
jmethodID GetYuzuDeviceGetAxes() {
return state.yuzu_input_device_get_axes;
return s_yuzu_input_device_get_axes;
}
jmethodID GetYuzuDeviceHasKeys() {
return state.yuzu_input_device_has_keys;
return s_yuzu_input_device_has_keys;
}
jmethodID GetAddNetPlayMessage() {
return state.add_netplay_message;
return s_add_netplay_message;
}
jmethodID ClearChat() {
return state.clear_chat;
return s_clear_chat;
}
#ifdef __cplusplus
@@ -437,20 +436,20 @@ namespace Common::Android {
// UnInitialize Android Storage
Common::FS::Android::UnRegisterCallbacks();
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);
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);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
@@ -464,7 +463,7 @@ namespace Common::Android {
#endif
void Initialize(JavaVM* vm, JNIEnv *env) {
state.java_vm = vm;
s_java_vm = vm;
InitFFmpegOnLoad(vm);
if (env->ExceptionCheck()) {
@@ -473,169 +472,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
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",
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",
"(Ljava/lang/String;Ljava/lang/String;)Z");
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z");
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
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");
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");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
state.game_constructor = env->GetMethodID(game_class, "<init>",
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
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");
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");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
state.pair_constructor =
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_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");
state.overlay_control_data_class =
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
state.overlay_control_data_constructor =
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
state.overlay_control_data_id_field =
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
state.overlay_control_data_enabled_field =
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
state.overlay_control_data_landscape_position_field =
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
state.overlay_control_data_portrait_position_field =
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
state.overlay_control_data_foldable_position_field =
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
state.overlay_control_data_individual_scale_field =
s_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");
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
state.patch_constructor = env->GetMethodID(
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
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;");
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;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
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");
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");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
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");
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");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
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");
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");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
state.player_input_constructor = env->GetMethodID(
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
state.player_input_buttons_field =
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
state.player_input_analogs_field =
s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
state.player_input_motions_field =
s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
state.player_input_vibration_enabled_field =
s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
state.player_input_vibration_strength_field =
s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
state.player_input_body_color_left_field =
s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
state.player_input_body_color_right_field =
s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
state.player_input_button_color_left_field =
s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
state.player_input_button_color_right_field =
s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
state.player_input_profile_name_field =
s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
state.player_input_use_system_vibrator_field =
s_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");
state.yuzu_input_device_interface =
s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
state.yuzu_input_device_get_name =
s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
state.yuzu_input_device_get_guid =
s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
state.yuzu_input_device_get_supports_vibration =
s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
state.yuzu_input_device_get_axes =
s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
state.yuzu_input_device_has_keys =
s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
+18 -4
View File
@@ -22,22 +22,36 @@ 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 std::bit_width(value) - 1;
return T(MostSignificantBit<T>(value));
}
template<typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr T Log2Ceil(const T value) {
return std::bit_width(value - 1);
const T log2_f = Log2Floor<T>(value);
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] T NextPow2(T value) {
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
}
template <size_t bit_index, typename T>
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool Bit(const T value) {
return (T(value >> bit_index) & T(1)) == T(1);
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
return ((value >> bit_index) & T(1)) == T(1);
}
} // namespace Common
+13 -5
View File
@@ -25,18 +25,26 @@ 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 ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
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;
}
template <size_t Bits>
struct HashCombineImpl {
template <typename T>
static inline T fn(T seed, T value) {
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,14 +12,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 N((D(number) + divisor - 1) / divisor);
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
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 N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
} // namespace Common
+28
View File
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <utility>
#include <boost/functional/hash.hpp>
namespace Common {
struct PairHash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
std::size_t seed = std::hash<T1>()(pair.first);
boost::hash_combine(seed, std::hash<T2>()(pair.second));
return seed;
}
};
template <typename T>
struct IdentityHash {
[[nodiscard]] size_t operator()(T value) const noexcept {
return static_cast<size_t>(value);
}
};
} // namespace Common
+16 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -19,12 +16,14 @@ namespace Common {
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
if (c >= 65 && c <= 70) {
return u8(c - 55);
return static_cast<u8>(c - 55);
}
if (c >= 97 && c <= 102) {
return u8(c - 87);
return static_cast<u8>(c - 87);
}
return u8(c - 48);
return static_cast<u8>(c - 48);
}
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
@@ -32,28 +31,35 @@ 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] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
} else {
for (std::size_t i = 0; i < 2 * Size; i += 2) {
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
}
return out;
}
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;
}
+6 -10
View File
@@ -10,7 +10,6 @@
#include <cstdlib>
#include <regex>
#include <thread>
#include <boost/algorithm/string/predicate.hpp>
#if defined(__ANDROID__)
#include <android/log.h>
@@ -96,10 +95,9 @@ 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) {
auto const level_name = GetLevelName(Level(i));
if (boost::iequals(sv, level_name))
const char* level_name = GetLevelName(Level(i));
if (Common::ComparePartialString(begin, end, level_name))
return Level(i);
}
return Level::Count;
@@ -107,10 +105,9 @@ 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) {
auto const level_name = GetLogClassName(Class(i));
if (boost::iequals(sv, level_name))
const char* level_name = GetLogClassName(Class(i));
if (Common::ComparePartialString(begin, end, level_name))
return Class(i);
}
return Class::Count;
@@ -123,13 +120,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
return false;
}
auto const sv = std::string_view{begin, level_separator};
auto const level = GetLevelByName(level_separator + 1, end);
const Level 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 (boost::iequals(sv, "*")) {
if (Common::ComparePartialString(begin, level_separator, "*")) {
instance.class_levels.fill(level);
return true;
}
+18 -20
View File
@@ -8,9 +8,6 @@
#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"
@@ -18,16 +15,17 @@
namespace Common {
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";
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";
/// A placeholder for empty param packages to avoid empty strings
/// (they may be recognized as "not set" by some frontend libraries like qt)
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
ParamPackage::ParamPackage(const std::string& serialized) {
if (serialized == EMPTY_PLACEHOLDER) {
@@ -35,20 +33,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
}
std::vector<std::string> pairs;
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
for (const std::string& pair : pairs) {
std::vector<std::string> key_value;
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
if (key_value.size() != 2) {
LOG_ERROR(Common, "invalid key pair {}", pair);
continue;
}
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);
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});
}
Set(key_value[0], std::move(key_value[1]));
@@ -65,10 +63,10 @@ std::string ParamPackage::Serialize() const {
for (const auto& pair : data) {
std::array<std::string, 2> key_value{{pair.first, pair.second}};
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);
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);
}
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
}
-3
View File
@@ -653,9 +653,6 @@ struct Values {
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
+1 -1
View File
@@ -150,7 +150,7 @@ ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, Scale
ENUM(AntiAliasing, None, Fxaa, Smaa);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
ENUM(AppletMode, HLE, LLE, Disabled);
ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
ENUM(GpuClock, Normal, Boost, Overclock)
ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge)
+51
View File
@@ -24,6 +24,22 @@
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())
@@ -64,6 +80,41 @@ 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());
+56 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
@@ -16,6 +16,12 @@
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'));
}
@@ -23,8 +29,37 @@ 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);
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension);
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
const std::string& dest);
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
@@ -38,13 +73,30 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
/**
* 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
+1
View File
@@ -13,6 +13,7 @@
#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"
+19 -12
View File
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
UNREACHABLE();
}
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
/// Initialization
system.RegisterCoreThread(core);
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
@@ -178,19 +178,26 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
auto& data = core_data[core];
data.host_context = Common::Fiber::ThreadToFiber();
// Cleanup
SCOPE_EXIT {
data.host_context->Exit();
};
// Running
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 (!gpu_barrier->Sync(token)) {
return;
}
data.host_context->Exit();
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
}
} // namespace Core
+2 -3
View File
@@ -8,7 +8,6 @@
#include <array>
#include <atomic>
#include <barrier>
#include <functional>
#include <memory>
#include <thread>
@@ -53,7 +52,7 @@ public:
}
void OnGpuReady() {
gpu_barrier->arrive_and_wait();
gpu_barrier->Sync();
}
void Initialize();
@@ -96,7 +95,7 @@ private:
static constexpr std::size_t max_cycle_runs = 5;
std::optional<std::barrier<>> gpu_barrier{};
std::optional<Common::Barrier> gpu_barrier{};
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
std::jthread host_thread;
+1 -2
View File
@@ -14,7 +14,6 @@
#include <tuple>
#include <vector>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/evp.h>
#include "common/fs/file.h"
@@ -623,7 +622,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
} else {
boost::algorithm::to_lower(out[0]);
out[0] = Common::ToLower(out[0]);
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
const auto& index = iter128->second;
const Key128 key = Common::HexStringToArray<16>(out[1]);
+5 -3
View File
@@ -6,7 +6,6 @@
#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"
@@ -42,13 +41,16 @@ 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 = boost::algorithm::to_upper_copy(name);
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
const std::string& name) {
const auto upper = Common::ToUpper(name);
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
if (dir->GetFile(fname) != nullptr) {
return dir->GetFile(fname);
}
}
return nullptr;
}
+3 -2
View File
@@ -8,7 +8,6 @@
#include <array>
#include <cstddef>
#include <cstring>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/assert.h"
#include "common/hex_util.h"
@@ -74,10 +73,12 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
#else
const auto subdirs = dir->GetSubdirectories();
for (const auto& subdir : subdirs) {
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
std::string dir_name = Common::ToLower(subdir->GetName());
if (dir_name == name) {
return subdir;
}
}
return nullptr;
#endif
}
+4 -4
View File
@@ -8,7 +8,6 @@
#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"
@@ -1415,10 +1414,11 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
continue;
}
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
if (ext == "nsp") {
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
if (extension == "nsp") {
ProcessNSP(file);
} else if (ext == "xci") {
} else if (extension == "xci") {
ProcessXCI(file);
}
}
+2 -3
View File
@@ -9,7 +9,6 @@
#include <regex>
#include <string>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/err.h>
#include <openssl/evp.h>
@@ -64,8 +63,8 @@ NAX::NAX(VirtualFile file_)
return;
}
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]});
const std::string two_dir = Common::ToUpper(std::string{match[1]});
const std::string nca_id = Common::ToLower(std::string{match[2]});
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
}
+2 -11
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,14 +19,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
// Virtual to Physical core map.
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>();
+30 -2
View File
@@ -16,6 +16,10 @@
#include "core/hle/service/ro/ro_types.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#ifdef HAS_NCE
#include "core/arm/nce/patcher.h"
#include "core/hle/kernel/k_shared_memory.h"
#endif
namespace Service::RO {
@@ -386,7 +390,7 @@ public:
R_SUCCEED();
}
Result MapManualLoadModuleMemory(u64* out_address, size_t context_id, u64 nro_address,
Result MapManualLoadModuleMemory(Kernel::KernelCore& kernel, u64* out_address, size_t context_id, u64 nro_address,
u64 nro_size, u64 bss_address, u64 bss_size) {
// Get context.
ProcessContext* context = this->GetContextById(context_id);
@@ -421,7 +425,31 @@ public:
R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
std::addressof(ro_size), std::addressof(rw_size),
nro_info->base_address, nro_size, bss_size));
#ifdef HAS_NCE
if (Settings::IsNceEnabled()) {
auto* process = context->GetProcess();
auto& memory = process->GetMemory();
std::vector<u8> image(total_size);
memory.ReadBlock(nro_info->base_address, image.data(), rx_size);
Kernel::CodeSet::Segment code{.size = static_cast<u32>(rx_size)};
Core::NCE::Patcher patch;
patch.PatchText(image, code);
patch.RelocateAndCopy(nro_info->base_address, code, image, nullptr);
const u64 patch_address = nro_info->base_address + total_size;
const size_t patch_size = patch.GetSectionSize();
constexpr auto permission = Kernel::Svc::MemoryPermission::ReadExecute;
auto* patch_memory = Kernel::KSharedMemory::Create(kernel);
R_TRY(patch_memory->Initialize(kernel, kernel.System().DeviceMemory(), process, permission, permission, patch_size));
std::memcpy(patch_memory->GetPointer(), image.data() + total_size, patch_size);
R_TRY(process->AddSharedMemory(kernel, patch_memory, patch_address, patch_size));
R_TRY(patch_memory->Map(*process, patch_address, patch_size, permission));
memory.WriteBlock(nro_info->base_address, image.data(), rx_size);
}
#endif
// Set NRO perms.
R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size,
rw_size + bss_size));
@@ -531,7 +559,7 @@ public:
Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address,
R_RETURN(m_ro->MapManualLoadModuleMemory(system.Kernel(), out_load_address.Get(), m_context_id, nro_address,
nro_size, bss_address, bss_size));
}
+2 -2
View File
@@ -10,7 +10,6 @@
#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"
@@ -171,7 +170,8 @@ FileType GuessFromFilename(const std::string& name) {
else if (name == "00")
return FileType::NCA;
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
auto const extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
if (extension == "nro")
return FileType::NRO;
else if (extension == "nso")
+1 -1
View File
@@ -757,7 +757,7 @@ struct Memory::Impl {
};
auto& gpu = system.GPU();
gpu_device_memory->ApplyOpOnPointer(
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size, true); });
}
Core::System& system;
+1 -2
View File
@@ -14,7 +14,6 @@
#include <regex>
#include <string>
#include <thread>
#include <boost/algorithm/string/trim.hpp>
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
@@ -133,7 +132,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
std::string line;
std::getline(file, line);
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
boost::trim(line);
line = Common::StripSpaces(line);
if (line.empty()) {
// An empty line marks start of the IP ban list
ban_list_type = true;
+1 -9
View File
@@ -271,12 +271,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"quality and performance consistency in some games."));
INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout."));
INSERT(Settings, rescale_hack, tr("Enable Legacy Rescale Pass"),
tr("May fix rescale issues in some games by relying on behavior from the previous "
"implementation.\n"
"Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and "
"grey texture flicker on Nvidia GPUs in Luigis Mansion 3."));
// Renderer (Extensions)
INSERT(Settings, dyna_state, tr("Extended Dynamic State"),
tr("Controls the number of features that can be used in Extended Dynamic State.\n"
@@ -342,9 +336,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"it bypasses such prompts and directly exits the emulation."));
INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"),
tr("Hides the mouse after 2.5s of inactivity."));
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
tr("Forcibly disables the use of the controller applet in emulated programs.\n"
"When a program attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings, check_for_updates, tr("Check for updates"),
tr("Whether or not to check for updates upon startup."));
@@ -380,6 +371,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
{
PAIR(AppletMode, HLE, tr("Custom frontend")),
PAIR(AppletMode, LLE, tr("Real applet")),
PAIR(AppletMode, Disabled, tr("Disabled")),
}});
translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(),
-2
View File
@@ -127,8 +127,6 @@ struct Values {
Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true};
Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
Category::UiGeneral};
// Set when Vulkan is known to crash the application
bool has_broken_vulkan = false;
+1 -2
View File
@@ -8,7 +8,6 @@
#include <string>
#include <QEventLoop>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include "common/httplib.h"
@@ -46,7 +45,7 @@ void DiscordImpl::Pause() {
std::string DiscordImpl::GetGameString(const std::string& title) {
// Convert to lowercase
std::string icon_name = boost::algorithm::to_lower_copy(title);
std::string icon_name = Common::ToLower(title);
// Replace spaces with dashes
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
@@ -301,7 +301,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
Optimization::TexturePass(env, program, normalized_host_info);
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
if (Settings::values.resolution_info.active) {
Optimization::RescalingPass(program);
}
Optimization::DeadCodeEliminationPass(program);
+4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,6 +31,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
void VendorWorkaroundPass(IR::Program& program);
void VerificationPass(const IR::Program& program);
bool HasBrokenPattern(const IR::Program& program);
// Dual Vertex
void VertexATransformPass(IR::Program& program);
@@ -67,18 +67,11 @@ void VisitMark(IR::Block& block, IR::Inst& inst) {
if (must_patch_outside) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, it};
if (Settings::values.rescale_hack.GetValue()) {
const IR::F32 new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_inst, up_factor)};
inst.ReplaceUsesWith(converted);
} else {
IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_bitcast, up_factor)};
inst.ReplaceUsesWith(converted);
}
IR::Inst* const new_inst{&*block.PrependNewInst(it, inst)};
const IR::F32 new_bitcast{ir.ConvertUToF(32, 32, IR::Value{new_inst})};
const IR::F32 up_factor{ir.FPRecip(ir.ResolutionDownFactor())};
const IR::Value converted{ir.FPMul(new_bitcast, up_factor)};
inst.ReplaceUsesWith(converted);
}
break;
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -76,4 +79,44 @@ void VendorWorkaroundPass(IR::Program& program) {
}
}
bool HasBrokenPattern(const IR::Program& program) {
if (program.stage != Stage::Fragment) {
return false;
}
for (const IR::Block* block : program.post_order_blocks) {
for (const IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::ShuffleIndex:
case IR::Opcode::ShuffleUp:
case IR::Opcode::ShuffleDown:
case IR::Opcode::ShuffleButterfly:
break;
default:
continue;
}
const IR::Value shuffle_arg{inst.Arg(0)};
if (shuffle_arg.IsImmediate()) {
continue;
}
const IR::Inst* bitcast{shuffle_arg.InstRecursive()};
if (!bitcast || bitcast->GetOpcode() != IR::Opcode::BitCastU32F32) {
continue;
}
const IR::Value bitcast_arg{bitcast->Arg(0)};
if (bitcast_arg.IsImmediate()) {
continue;
}
const IR::Inst* attribute{bitcast_arg.InstRecursive()};
if (!attribute || attribute->GetOpcode() != IR::Opcode::GetAttribute) {
continue;
}
const IR::Attribute attr{attribute->Arg(0).Attribute()};
if (attr == IR::Attribute::PositionX || attr == IR::Attribute::PositionY) {
return true;
}
}
}
return false;
}
} // namespace Shader::Optimization
+13 -3
View File
@@ -226,7 +226,17 @@ struct GPU::Impl {
}
/// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size) {
void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) {
VideoCore::RasterizerInterface* rasterizer = renderer->ReadRasterizer();
if (preserve_gpu_writes && rasterizer->MustFlushRegion(addr, size, VideoCommon::CacheType::BufferCache)) {
const u64 fence = RequestSyncOperation([rasterizer, addr, size] {
rasterizer->FlushRegion(addr, size, VideoCommon::CacheType::BufferCache);
rasterizer->OnCacheInvalidation(addr, size);
});
gpu_thread.TickGPU(is_async);
WaitForSyncOperation(fence);
return;
}
gpu_thread.InvalidateRegion(addr, size);
}
@@ -518,8 +528,8 @@ void GPU::FlushRegion(DAddr addr, u64 size) {
impl->FlushRegion(addr, size);
}
void GPU::InvalidateRegion(DAddr addr, u64 size) {
impl->InvalidateRegion(addr, size);
void GPU::InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes) {
impl->InvalidateRegion(addr, size, preserve_gpu_writes);
}
bool GPU::OnCPUWrite(DAddr addr, u64 size) {
+1 -1
View File
@@ -248,7 +248,7 @@ public:
void FlushRegion(DAddr addr, u64 size);
/// Notify rasterizer that any caches of the specified region should be invalidated
void InvalidateRegion(DAddr addr, u64 size);
void InvalidateRegion(DAddr addr, u64 size, bool preserve_gpu_writes = false);
/// Notify rasterizer that CPU is trying to write this area. It returns true if the area is
/// sensible, false otherwise, addr and size must be a valid combination
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-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 = std::bit_ceil(requested_size);
const auto next_pow2_size = Common::NextPow2(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 = std::bit_ceil(copy_size);
pbo_size = Common::NextPow2(copy_size);
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
}
// Copy from source to PBO
@@ -23,6 +23,7 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/ir_opt/passes.h"
#include "shader_recompiler/program_header.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
@@ -826,6 +827,11 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
}
if (device.GetDriverID() != VK_DRIVER_ID_NVIDIA_PROPRIETARY && Shader::Optimization::HasBrokenPattern(programs[index])) {
LOG_WARNING(Render_Vulkan, "0x{:016x}: Skipping shader with broken pattern", key.unique_hashes[index]);
return nullptr;
}
if (Settings::values.dump_guest_shaders) {
env.Dump(hash, key.unique_hashes[index]);
}
@@ -1011,7 +1011,7 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
if (buffers[level]) {
return *buffers[level];
}
const auto new_size = std::bit_ceil(needed_size);
const auto new_size = Common::NextPow2(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;
+1 -1
View File
@@ -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>>& selected_page_table) {
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& 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,6 +22,7 @@
#include <queue>
#include "common/common_types.h"
#include "common/hash.h"
#include "common/literals.h"
#include "common/lru_cache.h"
#include <ranges>
@@ -68,7 +69,7 @@ struct AsyncDecodeContext {
std::atomic_bool complete;
};
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>>;
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
class TextureCacheChannelInfo : public ChannelInfo {
public:
@@ -442,7 +443,7 @@ private:
u64 last_framebuffer_serial = 0;
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
::Common::unordered_map<u64, std::vector<ImageMapId>> page_table;
::Common::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
DAddr virtual_invalid_space{};
+3 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,7 +11,6 @@
class QCheckBox;
class QLineEdit;
class QComboBox;
class QDateTimeEdit;
namespace Core {
class System;
+8 -4
View File
@@ -140,7 +140,12 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
if (combobox_enumerations.contains(type)) {
enumeration = &combobox_enumerations.at(type);
for (const auto& [id, name] : *enumeration) {
combobox->addItem(name);
if (type == Settings::EnumMetadata<Settings::AppletMode>::Index() &&
id == static_cast<u32>(Settings::AppletMode::Disabled) &&
setting.Id() != Settings::values.controller_applet_mode.Id()) {
continue;
}
combobox->addItem(name, id);
}
} else {
return combobox;
@@ -158,9 +163,8 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
const u32 setting_value = std::strtoul(setting.ToString().c_str(), nullptr, 0);
combobox->setCurrentIndex(find_index(setting_value));
serializer = [this, enumeration]() {
int current = combobox->currentIndex();
return std::to_string(enumeration->at(current).first);
serializer = [this]() {
return std::to_string(combobox->currentData().toUInt());
};
restore_func = [this, find_index]() {
+15 -11
View File
@@ -12,25 +12,29 @@ namespace Yuzu {
inline bool ContainsAllWords(const QString& haystack, const QString& userinput) {
const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
return std::all_of(userinput_split.begin(), userinput_split.end(),
[&haystack](const QString& s) { return haystack.contains(s); });
return std::all_of(userinput_split.begin(), userinput_split.end(), [&haystack](const QString& s) {
return haystack.contains(s);
});
}
inline bool FilterMatches(const QString& filter, const QStandardItem* item) {
if (filter.isEmpty())
return true;
auto const sluggify = [](const QString& s) {
QString o(s.normalized(QString::NormalizationForm_D).toLower());
return o.replace(QRegularExpression(QStringLiteral("[^a-z\\s]")), QStringLiteral(""));
};
const auto norm_filter = sluggify(filter);
const auto program_id = item->data(GameListItemPath::ProgramIdRole).toULongLong();
const QString file_path = item->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_path = sluggify(item->data(GameListItemPath::FullPathRole).toString());
const QString file_title = sluggify(item->data(GameListItemPath::TitleRole).toString());
const QString file_program_id = QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
const QString file_name =
file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title;
return Yuzu::ContainsAllWords(file_name, filter) ||
(file_program_id.size() == 16 && file_program_id.contains(filter));
const QString file_name = sluggify(file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title);
return (ContainsAllWords(file_name, norm_filter)
|| ContainsAllWords(file_title, norm_filter))
|| (file_program_id.size() == 16 && file_program_id.contains(norm_filter));
}
} // namespace Yuzu
+1 -1
View File
@@ -1786,7 +1786,7 @@ bool MainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPar
QtCommon::system->SetFrontendAppletSet({
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
(UISettings::values.controller_applet_disabled.GetValue() == true)
(Settings::values.controller_applet_mode.GetValue() == Settings::AppletMode::Disabled)
? nullptr
: std::make_unique<QtControllerSelector>(*this), // Controller Selector
std::make_unique<QtErrorDisplay>(*this), // Error Display