summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt75
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt124
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt987
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt188
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt175
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt38
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.cpp9
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.h3
-rw-r--r--src/android/app/src/main/jni/android_config.cpp73
-rw-r--r--src/android/app/src/main/jni/android_config.h3
-rw-r--r--src/android/app/src/main/jni/android_settings.h30
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp79
-rw-r--r--src/android/app/src/main/jni/id_cache.h12
-rw-r--r--src/android/app/src/main/jni/native_config.cpp70
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml2
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml5
-rw-r--r--src/android/app/src/main/res/values/arrays.xml39
-rw-r--r--src/android/app/src/main/res/values/integers.xml204
-rw-r--r--src/android/app/src/main/res/values/strings.xml15
-rw-r--r--src/android/build.gradle.kts2
-rw-r--r--src/audio_core/device/device_session.cpp4
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/assert.cpp3
-rw-r--r--src/common/heap_tracker.cpp281
-rw-r--r--src/common/heap_tracker.h98
-rw-r--r--src/common/host_memory.cpp10
-rw-r--r--src/common/host_memory.h11
-rw-r--r--src/common/logging/backend.cpp17
-rw-r--r--src/common/logging/backend.h3
-rw-r--r--src/common/ring_buffer.h2
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/core/CMakeLists.txt23
-rw-r--r--src/core/arm/arm_interface.cpp2
-rw-r--r--src/core/arm/arm_interface.h2
-rw-r--r--src/core/arm/debug.cpp14
-rw-r--r--src/core/arm/debug.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp49
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/arm/nce/arm_nce.cpp2
-rw-r--r--src/core/arm/nce/interpreter_visitor.cpp5
-rw-r--r--src/core/arm/nce/visitor_base.h6
-rw-r--r--src/core/core.cpp73
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/core_timing.cpp48
-rw-r--r--src/core/core_timing.h27
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/program_metadata.cpp6
-rw-r--r--src/core/file_sys/program_metadata.h13
-rw-r--r--src/core/hid/hid_types.h1
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp19
-rw-r--r--src/core/hle/kernel/k_auto_object_container.cpp9
-rw-r--r--src/core/hle/kernel/k_auto_object_container.h31
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp6
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h3
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp26
-rw-r--r--src/core/hle/kernel/k_page_table_base.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp56
-rw-r--r--src/core/hle/kernel/k_process.h15
-rw-r--r--src/core/hle/kernel/k_server_session.cpp1424
-rw-r--r--src/core/hle/kernel/k_server_session.h15
-rw-r--r--src/core/hle/kernel/k_session.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp11
-rw-r--r--src/core/hle/kernel/k_thread.h6
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp36
-rw-r--r--src/core/hle/kernel/kernel.h7
-rw-r--r--src/core/hle/kernel/message_buffer.h20
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp1
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp6
-rw-r--r--src/core/hle/kernel/svc_results.h2
-rw-r--r--src/core/hle/service/am/am.cpp12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/audio/hwopus.cpp16
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h95
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.h (renamed from src/core/hle/service/hid/controllers/stubbed.h)8
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h6
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp7
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h10
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.cpp64
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.h34
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.h27
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h5
-rw-r--r--src/core/hle/service/hid/controllers/home_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/home_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h5
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1003
-rw-r--r--src/core/hle/service/hid/controllers/npad.h166
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_data.cpp228
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_data.h88
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_resource.cpp685
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_resource.h132
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp31
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/controllers/types/npad_types.h3
-rw-r--r--src/core/hle/service/hid/controllers/types/shared_memory_format.h (renamed from src/core/hle/service/hid/controllers/shared_memory_format.h)2
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.h27
-rw-r--r--src/core/hle/service/hid/errors.h32
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/core/hle/service/hid/hid_server.cpp233
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp38
-rw-r--r--src/core/hle/service/hid/hid_util.h8
-rw-r--r--src/core/hle/service/hid/hidbus.cpp11
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp8
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp161
-rw-r--r--src/core/hle/service/hid/resource_manager.h28
-rw-r--r--src/core/hle/service/hle_ipc.cpp20
-rw-r--r--src/core/hle/service/hle_ipc.h20
-rw-r--r--src/core/hle/service/ipc_helpers.h4
-rw-r--r--src/core/hle/service/jit/jit.cpp69
-rw-r--r--src/core/hle/service/jit/jit_code_memory.cpp54
-rw-r--r--src/core/hle/service/jit/jit_code_memory.h49
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp8
-rw-r--r--src/core/hle/service/ro/ro.cpp12
-rw-r--r--src/core/hle/service/server_manager.cpp21
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp12
-rw-r--r--src/core/hle/service/set/set_sys.h1
-rw-r--r--src/core/hle/service/sm/sm.cpp36
-rw-r--r--src/core/hle/service/sm/sm.h8
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp7
-rw-r--r--src/core/memory.cpp114
-rw-r--r--src/core/memory.h10
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/tools/freezer.cpp17
-rw-r--r--src/core/tools/freezer.h2
-rw-r--r--src/frontend_common/config.cpp184
-rw-r--r--src/frontend_common/config.h39
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp5
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp22
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp81
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h2
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp6
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp12
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp25
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h107
-rw-r--r--src/tests/common/host_memory.cpp99
-rw-r--r--src/tests/core/core_timing.cpp14
-rw-r--r--src/video_core/engines/maxwell_3d.cpp24
-rw-r--r--src/video_core/engines/maxwell_dma.cpp4
-rw-r--r--src/video_core/macro/macro_hle.cpp7
-rw-r--r--src/video_core/query_cache.h39
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp54
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp63
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/shader_environment.cpp35
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/video_core/texture_cache/decode_bc.cpp50
-rw-r--r--src/video_core/texture_cache/decode_bc.h2
-rw-r--r--src/video_core/texture_cache/util.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp21
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp94
-rw-r--r--src/yuzu/configuration/configure_graphics.h4
-rw-r--r--src/yuzu/configuration/qt_config.cpp69
-rw-r--r--src/yuzu/main.cpp4
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp3
-rw-r--r--src/yuzu_cmd/sdl_config.cpp26
223 files changed, 7289 insertions, 3004 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index f763c657e..53aafa08c 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -10,7 +10,7 @@ plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
- kotlin("plugin.serialization") version "1.8.21"
+ kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 9b08f008d..93c8ce922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
+import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat
@@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
+ override fun onStop() {
+ super.onStop()
+ NativeConfig.saveGlobalConfig()
+ }
+
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 16f06cd0a..86bd33672 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -18,7 +18,14 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
RENDERER_DEBUG("debug"),
PICTURE_IN_PICTURE("picture_in_picture"),
- USE_CUSTOM_RTC("custom_rtc_enabled");
+ USE_CUSTOM_RTC("custom_rtc_enabled"),
+ BLACK_BACKGROUNDS("black_backgrounds"),
+ JOYSTICK_REL_CENTER("joystick_rel_center"),
+ DPAD_SLIDE("dpad_slide"),
+ HAPTIC_FEEDBACK("haptic_feedback"),
+ SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
+ SHOW_INPUT_OVERLAY("show_input_overlay"),
+ TOUCHSCREEN("touchscreen");
override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 21e4e1afd..16fb87614 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -18,7 +18,12 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_ANTI_ALIASING("anti_aliasing"),
RENDERER_SCREEN_LAYOUT("screen_layout"),
RENDERER_ASPECT_RATIO("aspect_ratio"),
- AUDIO_OUTPUT_ENGINE("output_engine");
+ AUDIO_OUTPUT_ENGINE("output_engine"),
+ MAX_ANISOTROPY("max_anisotropy"),
+ THEME("theme"),
+ THEME_MODE("theme_mode"),
+ OVERLAY_SCALE("control_scale"),
+ OVERLAY_OPACITY("control_opacity");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 9551fc05e..fee80bb21 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -15,18 +15,10 @@ object Settings {
SECTION_DEBUG(R.string.preferences_debug);
}
+ const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
- const val PREF_OVERLAY_VERSION = "OverlayVersion"
- const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
- const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
- const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
- val overlayLayoutPrefs = listOf(
- PREF_LANDSCAPE_OVERLAY_VERSION,
- PREF_PORTRAIT_OVERLAY_VERSION,
- PREF_FOLDABLE_OVERLAY_VERSION
- )
-
+ // Deprecated input overlay preference keys
const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@@ -47,23 +39,12 @@ object Settings {
const val PREF_BUTTON_STICK_R = "buttonToggle14"
const val PREF_BUTTON_HOME = "buttonToggle15"
const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
-
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
-
- const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
- const val PREF_THEME = "Theme"
- const val PREF_THEME_MODE = "ThemeMode"
- const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
-
val overlayPreferences = listOf(
- PREF_OVERLAY_VERSION,
- PREF_CONTROL_SCALE,
- PREF_CONTROL_OPACITY,
- PREF_TOUCH_ENABLED,
PREF_BUTTON_A,
PREF_BUTTON_B,
PREF_BUTTON_X,
@@ -83,7 +64,33 @@ object Settings {
PREF_BUTTON_STICK_R
)
- const val LayoutOption_Unspecified = 0
- const val LayoutOption_MobilePortrait = 4
- const val LayoutOption_MobileLandscape = 5
+ // Deprecated layout preference keys
+ const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
+ const val PREF_PORTRAIT_SUFFIX = "_Portrait"
+ const val PREF_FOLDABLE_SUFFIX = "_Foldable"
+ val overlayLayoutSuffixes = listOf(
+ PREF_LANDSCAPE_SUFFIX,
+ PREF_PORTRAIT_SUFFIX,
+ PREF_FOLDABLE_SUFFIX
+ )
+
+ // Deprecated theme preference keys
+ const val PREF_THEME = "Theme"
+ const val PREF_THEME_MODE = "ThemeMode"
+ const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
+
+ enum class EmulationOrientation(val int: Int) {
+ Unspecified(0),
+ SensorLandscape(5),
+ Landscape(1),
+ ReverseLandscape(2),
+ SensorPortrait(6),
+ Portrait(4),
+ ReversePortrait(3);
+
+ companion object {
+ fun from(int: Int): EmulationOrientation =
+ entries.firstOrNull { it.int == int } ?: Unspecified
+ }
+ }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 2e97aee2c..12f7aa1ab 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -245,6 +245,15 @@ abstract class SettingsItem(
)
put(
SingleChoiceSetting(
+ IntSetting.MAX_ANISOTROPY,
+ R.string.anisotropic_filtering,
+ R.string.anisotropic_filtering_description,
+ R.array.anisoEntries,
+ R.array.anisoValues
+ )
+ )
+ put(
+ SingleChoiceSetting(
IntSetting.AUDIO_OUTPUT_ENGINE,
R.string.audio_output_engine,
0,
@@ -298,6 +307,7 @@ abstract class SettingsItem(
override val key: String = FASTMEM_COMBINED
override val isRuntimeModifiable: Boolean = false
+ override val pairedSettingKey = BooleanSetting.CPU_DEBUG_MODE.key
override val defaultValue: Boolean = true
override val isSwitchable: Boolean = true
override var global: Boolean
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index a7e965589..2ad2f4966 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,10 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.ui
-import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
-import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -29,9 +27,6 @@ class SettingsFragmentPresenter(
) {
private var settingsList = ArrayList<SettingsItem>()
- private val preferences: SharedPreferences
- get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
// Extension for altering settings list based on each setting's properties
fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!!
@@ -149,6 +144,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key)
add(IntSetting.RENDERER_ANTI_ALIASING.key)
+ add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
@@ -169,25 +165,19 @@ class SettingsFragmentPresenter(
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting {
- override fun getInt(needsGlobal: Boolean): Int =
- preferences.getInt(Settings.PREF_THEME, 0)
-
+ override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
override fun setInt(value: Int) {
- preferences.edit()
- .putInt(Settings.PREF_THEME, value)
- .apply()
+ IntSetting.THEME.setInt(value)
settingsViewModel.setShouldRecreate(true)
}
- override val key: String = Settings.PREF_THEME
- override val isRuntimeModifiable: Boolean = false
- override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
- override val defaultValue: Int = 0
- override fun reset() {
- preferences.edit()
- .putInt(Settings.PREF_THEME, defaultValue)
- .apply()
- }
+ override val key: String = IntSetting.THEME.key
+ override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ IntSetting.THEME.getValueAsString()
+
+ override val defaultValue: Int = IntSetting.THEME.defaultValue
+ override fun reset() = IntSetting.THEME.setInt(defaultValue)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -213,24 +203,22 @@ class SettingsFragmentPresenter(
}
val themeMode: AbstractIntSetting = object : AbstractIntSetting {
- override fun getInt(needsGlobal: Boolean): Int =
- preferences.getInt(Settings.PREF_THEME_MODE, -1)
-
+ override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
override fun setInt(value: Int) {
- preferences.edit()
- .putInt(Settings.PREF_THEME_MODE, value)
- .apply()
+ IntSetting.THEME_MODE.setInt(value)
settingsViewModel.setShouldRecreate(true)
}
- override val key: String = Settings.PREF_THEME_MODE
- override val isRuntimeModifiable: Boolean = false
- override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
- override val defaultValue: Int = -1
+ override val key: String = IntSetting.THEME_MODE.key
+ override val isRuntimeModifiable: Boolean =
+ IntSetting.THEME_MODE.isRuntimeModifiable
+
+ override fun getValueAsString(needsGlobal: Boolean): String =
+ IntSetting.THEME_MODE.getValueAsString()
+
+ override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
override fun reset() {
- preferences.edit()
- .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
- .apply()
+ IntSetting.THEME_MODE.setInt(defaultValue)
settingsViewModel.setShouldRecreate(true)
}
}
@@ -247,25 +235,24 @@ class SettingsFragmentPresenter(
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
- preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
+ BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
override fun setBoolean(value: Boolean) {
- preferences.edit()
- .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
- .apply()
+ BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
settingsViewModel.setShouldRecreate(true)
}
- override val key: String = Settings.PREF_BLACK_BACKGROUNDS
- override val isRuntimeModifiable: Boolean = false
+ override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
+ override val isRuntimeModifiable: Boolean =
+ BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
+
override fun getValueAsString(needsGlobal: Boolean): String =
- getBoolean().toString()
+ BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
- override val defaultValue: Boolean = false
+ override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
override fun reset() {
- preferences.edit()
- .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
- .apply()
+ BooleanSetting.BLACK_BACKGROUNDS
+ .setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
settingsViewModel.setShouldRecreate(true)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 0dce8ad8d..816336820 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -149,7 +149,7 @@ class AddonsFragment : Fragment() {
}
val isValid = externalAddonDirectory.listFiles()
- .any { AddonUtil.validAddonDirectories.contains(it.name) }
+ .any { AddonUtil.validAddonDirectories.contains(it.name?.lowercase()) }
val errorMessage = MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.invalid_directory,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index d7b38f62d..9efc1705d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
-import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.net.Uri
@@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
-import androidx.preference.PreferenceManager
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
@@ -46,22 +44,23 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel
-import org.yuzu.yuzu_emu.overlay.InputOverlay
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.*
import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
- private lateinit var preferences: SharedPreferences
private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null
@@ -101,6 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ updateOrientation()
val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null
@@ -141,7 +141,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
- preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
emulationState = EmulationState(game.path)
}
@@ -382,24 +381,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
updateScreenLayout()
+ val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
if (emulationActivity?.isInPictureInPictureMode == true) {
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
}
- if (EmulationMenuSettings.showOverlay) {
+ if (showInputOverlay) {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
} else {
- if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
+ if (showInputOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.visibility = View.VISIBLE
} else {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
+ binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
} else {
- binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
+ binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
}
}
}
@@ -423,17 +423,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun resetInputOverlay() {
- preferences.edit()
- .remove(Settings.PREF_CONTROL_SCALE)
- .remove(Settings.PREF_CONTROL_OPACITY)
- .apply()
+ IntSetting.OVERLAY_SCALE.reset()
+ IntSetting.OVERLAY_OPACITY.reset()
binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
}
}
private fun updateShowFpsOverlay() {
- if (EmulationMenuSettings.showFps) {
+ if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
val SYSTEM_FPS = 0
val FPS = 1
val FRAMETIME = 2
@@ -462,13 +460,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
- it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.getInt()) {
- Settings.LayoutOption_MobileLandscape ->
+ val orientationSetting =
+ EmulationOrientation.from(IntSetting.RENDERER_SCREEN_LAYOUT.getInt())
+ it.requestedOrientation = when (orientationSetting) {
+ EmulationOrientation.Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ EmulationOrientation.SensorLandscape ->
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
- Settings.LayoutOption_MobilePortrait ->
- ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
- Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- else -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+
+ EmulationOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ EmulationOrientation.ReverseLandscape ->
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+
+ EmulationOrientation.SensorPortrait ->
+ ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+
+ EmulationOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ EmulationOrientation.ReversePortrait ->
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
}
}
}
@@ -496,7 +504,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.inGameMenu.layoutParams.height = it.bounds.bottom
isInFoldableLayout = true
- binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
+ binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
}
}
it.isSeparating
@@ -535,18 +543,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
popup.menu.apply {
- findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps
- findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter
- findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide
- findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
- findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback
+ findItem(R.id.menu_toggle_fps).isChecked =
+ BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
+ findItem(R.id.menu_rel_stick_center).isChecked =
+ BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
+ findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
+ findItem(R.id.menu_show_overlay).isChecked =
+ BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
+ findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
+ findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
}
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_toggle_fps -> {
it.isChecked = !it.isChecked
- EmulationMenuSettings.showFps = it.isChecked
+ BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
updateShowFpsOverlay()
true
}
@@ -564,11 +576,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
R.id.menu_toggle_controls -> {
- val preferences =
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
- val optionsArray = BooleanArray(Settings.overlayPreferences.size)
- Settings.overlayPreferences.forEachIndexed { i, _ ->
- optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ val optionsArray = BooleanArray(overlayControlData.size)
+ overlayControlData.forEachIndexed { i, _ ->
+ optionsArray[i] = overlayControlData.firstOrNull { data ->
+ OverlayControl.entries[i].id == data.id
+ }?.enabled == true
}
val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -577,11 +590,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.array.gamepadButtons,
optionsArray
) { _, indexSelected, isChecked ->
- preferences.edit()
- .putBoolean("buttonToggle$indexSelected", isChecked)
- .apply()
+ overlayControlData.firstOrNull { data ->
+ OverlayControl.entries[indexSelected].id == data.id
+ }?.enabled = isChecked
}
.setPositiveButton(android.R.string.ok) { _, _ ->
+ NativeConfig.setOverlayControlData(overlayControlData)
+ NativeConfig.saveGlobalConfig()
binding.surfaceInputOverlay.refreshControls()
}
.setNegativeButton(android.R.string.cancel, null)
@@ -592,12 +607,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
.setOnClickListener {
val isChecked = !optionsArray[0]
- Settings.overlayPreferences.forEachIndexed { i, _ ->
+ overlayControlData.forEachIndexed { i, _ ->
optionsArray[i] = isChecked
dialog.listView.setItemChecked(i, isChecked)
- preferences.edit()
- .putBoolean("buttonToggle$i", isChecked)
- .apply()
+ overlayControlData[i].enabled = isChecked
}
}
true
@@ -605,26 +618,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_show_overlay -> {
it.isChecked = !it.isChecked
- EmulationMenuSettings.showOverlay = it.isChecked
+ BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
binding.surfaceInputOverlay.refreshControls()
true
}
R.id.menu_rel_stick_center -> {
it.isChecked = !it.isChecked
- EmulationMenuSettings.joystickRelCenter = it.isChecked
+ BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
true
}
R.id.menu_dpad_slide -> {
it.isChecked = !it.isChecked
- EmulationMenuSettings.dpadSlide = it.isChecked
+ BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
true
}
R.id.menu_haptics -> {
it.isChecked = !it.isChecked
- EmulationMenuSettings.hapticFeedback = it.isChecked
+ BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
+ true
+ }
+
+ R.id.menu_touchscreen -> {
+ it.isChecked = !it.isChecked
+ BooleanSetting.TOUCHSCREEN.setBoolean(it.isChecked)
true
}
@@ -644,7 +663,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity")
private fun startConfiguringControls() {
// Lock the current orientation to prevent editing inconsistencies
- if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
+ if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
emulationActivity?.let {
it.requestedOrientation =
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -662,11 +681,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing
- if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
+ if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
emulationActivity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
+ NativeConfig.saveGlobalConfig()
}
@SuppressLint("SetTextI18n")
@@ -675,7 +695,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
adjustBinding.apply {
inputScaleSlider.apply {
valueTo = 150F
- value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
+ value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputScaleValue.text = "${value.toInt()}%"
@@ -685,7 +705,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
inputOpacitySlider.apply {
valueTo = 100F
- value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
+ value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputOpacityValue.text = "${value.toInt()}%"
@@ -709,16 +729,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun setControlScale(scale: Int) {
- preferences.edit()
- .putInt(Settings.PREF_CONTROL_SCALE, scale)
- .apply()
+ IntSetting.OVERLAY_SCALE.setInt(scale)
binding.surfaceInputOverlay.refreshControls()
}
private fun setControlOpacity(opacity: Int) {
- preferences.edit()
- .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
- .apply()
+ IntSetting.OVERLAY_OPACITY.setInt(opacity)
binding.surfaceInputOverlay.refreshControls()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index b1d3c0040..b04d1208f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -445,7 +445,8 @@ class GamePropertiesFragment : Fragment() {
val zipResult = FileUtil.zipFromInternalStorage(
File(saveLocation),
saveLocation.replaceAfterLast("/", ""),
- BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
+ BufferedOutputStream(requireContext().contentResolver.openOutputStream(result)),
+ compression = false
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index d19f20dc2..5ae05b5cc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -167,13 +167,14 @@ class GamesViewModel : ViewModel() {
}
}
- fun onCloseGameFoldersFragment() =
+ fun onCloseGameFoldersFragment() {
+ NativeConfig.saveGlobalConfig()
viewModelScope.launch {
withContext(Dispatchers.IO) {
- NativeConfig.saveGlobalConfig()
getGameDirs(true)
}
}
+ }
private fun getGameDirs(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index a13faf3c7..c87486c90 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -21,7 +21,6 @@ import android.view.View
import android.view.View.OnTouchListener
import android.view.WindowInsets
import androidx.core.content.ContextCompat
-import androidx.preference.PreferenceManager
import androidx.window.layout.WindowMetricsCalculator
import kotlin.math.max
import kotlin.math.min
@@ -29,9 +28,12 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
import org.yuzu.yuzu_emu.NativeLibrary.StickType
import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.IntSetting
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
+import org.yuzu.yuzu_emu.utils.NativeConfig
/**
* Draws the interactive input overlay on top of the
@@ -51,23 +53,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private lateinit var windowInsets: WindowInsets
- var layout = LANDSCAPE
+ var layout = OverlayLayout.Landscape
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
windowInsets = rootWindowInsets
- val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
- if (overlayVersion != OVERLAY_VERSION) {
- resetAllLayouts()
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ if (overlayControlData.isEmpty()) {
+ populateDefaultConfig()
} else {
- val layoutIndex = overlayLayouts.indexOf(layout)
- val currentLayoutVersion =
- preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
- if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
- resetCurrentLayout()
- }
+ checkForNewControls(overlayControlData)
}
// Load the controls.
@@ -123,7 +120,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
for (dpad in overlayDpads) {
- if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide)) {
+ if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
continue
}
NativeLibrary.onGamePadButtonEvent(
@@ -174,7 +171,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
invalidate()
}
- if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
+ if (!BooleanSetting.TOUCHSCREEN.getBoolean()) {
return true
}
@@ -211,7 +208,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
private fun playHaptics(event: MotionEvent) {
- if (EmulationMenuSettings.hapticFeedback) {
+ if (BooleanSetting.HAPTIC_FEEDBACK.getBoolean()) {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN ->
@@ -255,10 +252,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_DOWN ->
// If no button is being moved now, remember the currently touched button to move.
if (buttonBeingConfigured == null &&
- button.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ button.bounds.contains(fingerPositionX, fingerPositionY)
) {
buttonBeingConfigured = button
buttonBeingConfigured!!.onConfigureTouch(event)
@@ -274,7 +268,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
// Persist button position by saving new place.
saveControlPosition(
- buttonBeingConfigured!!.prefId,
+ buttonBeingConfigured!!.overlayControlData.id,
buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(),
layout
@@ -307,7 +301,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
// Persist button position by saving new place.
saveControlPosition(
- Settings.PREF_BUTTON_DPAD,
+ OverlayControl.COMBINED_DPAD.id,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
layout
@@ -321,10 +315,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
when (event.action) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
- joystick.bounds.contains(
- fingerPositionX,
- fingerPositionY
- )
+ joystick.bounds.contains(fingerPositionX, fingerPositionY)
) {
joystickBeingConfigured = joystick
joystickBeingConfigured!!.onConfigureTouch(event)
@@ -351,231 +342,257 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
return true
}
- private fun addOverlayControls(layout: String) {
+ private fun addOverlayControls(layout: OverlayLayout) {
val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
- if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_a,
- R.drawable.facebutton_a_depressed,
- ButtonType.BUTTON_A,
- Settings.PREF_BUTTON_A,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_b,
- R.drawable.facebutton_b_depressed,
- ButtonType.BUTTON_B,
- Settings.PREF_BUTTON_B,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_x,
- R.drawable.facebutton_x_depressed,
- ButtonType.BUTTON_X,
- Settings.PREF_BUTTON_X,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_y,
- R.drawable.facebutton_y_depressed,
- ButtonType.BUTTON_Y,
- Settings.PREF_BUTTON_Y,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.l_shoulder,
- R.drawable.l_shoulder_depressed,
- ButtonType.TRIGGER_L,
- Settings.PREF_BUTTON_L,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.r_shoulder,
- R.drawable.r_shoulder_depressed,
- ButtonType.TRIGGER_R,
- Settings.PREF_BUTTON_R,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.zl_trigger,
- R.drawable.zl_trigger_depressed,
- ButtonType.TRIGGER_ZL,
- Settings.PREF_BUTTON_ZL,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.zr_trigger,
- R.drawable.zr_trigger_depressed,
- ButtonType.TRIGGER_ZR,
- Settings.PREF_BUTTON_ZR,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_plus,
- R.drawable.facebutton_plus_depressed,
- ButtonType.BUTTON_PLUS,
- Settings.PREF_BUTTON_PLUS,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_minus,
- R.drawable.facebutton_minus_depressed,
- ButtonType.BUTTON_MINUS,
- Settings.PREF_BUTTON_MINUS,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) {
- overlayDpads.add(
- initializeOverlayDpad(
- context,
- windowSize,
- R.drawable.dpad_standard,
- R.drawable.dpad_standard_cardinal_depressed,
- R.drawable.dpad_standard_diagonal_depressed,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_STICK_L, true)) {
- overlayJoysticks.add(
- initializeOverlayJoystick(
- context,
- windowSize,
- R.drawable.joystick_range,
- R.drawable.joystick,
- R.drawable.joystick_depressed,
- StickType.STICK_L,
- ButtonType.STICK_L,
- Settings.PREF_STICK_L,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_STICK_R, true)) {
- overlayJoysticks.add(
- initializeOverlayJoystick(
- context,
- windowSize,
- R.drawable.joystick_range,
- R.drawable.joystick,
- R.drawable.joystick_depressed,
- StickType.STICK_R,
- ButtonType.STICK_R,
- Settings.PREF_STICK_R,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_home,
- R.drawable.facebutton_home_depressed,
- ButtonType.BUTTON_HOME,
- Settings.PREF_BUTTON_HOME,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.facebutton_screenshot,
- R.drawable.facebutton_screenshot_depressed,
- ButtonType.BUTTON_CAPTURE,
- Settings.PREF_BUTTON_SCREENSHOT,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.button_l3,
- R.drawable.button_l3_depressed,
- ButtonType.STICK_L,
- Settings.PREF_BUTTON_STICK_L,
- layout
- )
- )
- }
- if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) {
- overlayButtons.add(
- initializeOverlayButton(
- context,
- windowSize,
- R.drawable.button_r3,
- R.drawable.button_r3_depressed,
- ButtonType.STICK_R,
- Settings.PREF_BUTTON_STICK_R,
- layout
- )
- )
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ for (data in overlayControlData) {
+ if (!data.enabled) {
+ continue
+ }
+
+ val position = data.positionFromLayout(layout)
+ when (data.id) {
+ OverlayControl.BUTTON_A.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_a,
+ R.drawable.facebutton_a_depressed,
+ ButtonType.BUTTON_A,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_B.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_b,
+ R.drawable.facebutton_b_depressed,
+ ButtonType.BUTTON_B,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_X.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_x,
+ R.drawable.facebutton_x_depressed,
+ ButtonType.BUTTON_X,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_Y.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_y,
+ R.drawable.facebutton_y_depressed,
+ ButtonType.BUTTON_Y,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_PLUS.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_plus,
+ R.drawable.facebutton_plus_depressed,
+ ButtonType.BUTTON_PLUS,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_MINUS.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_minus,
+ R.drawable.facebutton_minus_depressed,
+ ButtonType.BUTTON_MINUS,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_HOME.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_home,
+ R.drawable.facebutton_home_depressed,
+ ButtonType.BUTTON_HOME,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_CAPTURE.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.facebutton_screenshot,
+ R.drawable.facebutton_screenshot_depressed,
+ ButtonType.BUTTON_CAPTURE,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_L.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.l_shoulder,
+ R.drawable.l_shoulder_depressed,
+ ButtonType.TRIGGER_L,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_R.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.r_shoulder,
+ R.drawable.r_shoulder_depressed,
+ ButtonType.TRIGGER_R,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_ZL.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.zl_trigger,
+ R.drawable.zl_trigger_depressed,
+ ButtonType.TRIGGER_ZL,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_ZR.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.zr_trigger,
+ R.drawable.zr_trigger_depressed,
+ ButtonType.TRIGGER_ZR,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_STICK_L.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.button_l3,
+ R.drawable.button_l3_depressed,
+ ButtonType.STICK_L,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.BUTTON_STICK_R.id -> {
+ overlayButtons.add(
+ initializeOverlayButton(
+ context,
+ windowSize,
+ R.drawable.button_r3,
+ R.drawable.button_r3_depressed,
+ ButtonType.STICK_R,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.STICK_L.id -> {
+ overlayJoysticks.add(
+ initializeOverlayJoystick(
+ context,
+ windowSize,
+ R.drawable.joystick_range,
+ R.drawable.joystick,
+ R.drawable.joystick_depressed,
+ StickType.STICK_L,
+ ButtonType.STICK_L,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.STICK_R.id -> {
+ overlayJoysticks.add(
+ initializeOverlayJoystick(
+ context,
+ windowSize,
+ R.drawable.joystick_range,
+ R.drawable.joystick,
+ R.drawable.joystick_depressed,
+ StickType.STICK_R,
+ ButtonType.STICK_R,
+ data,
+ position
+ )
+ )
+ }
+
+ OverlayControl.COMBINED_DPAD.id -> {
+ overlayDpads.add(
+ initializeOverlayDpad(
+ context,
+ windowSize,
+ R.drawable.dpad_standard,
+ R.drawable.dpad_standard_cardinal_depressed,
+ R.drawable.dpad_standard_diagonal_depressed,
+ position
+ )
+ )
+ }
+ }
}
}
@@ -586,313 +603,87 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
overlayJoysticks.clear()
// Add all the enabled overlay items back to the HashSet.
- if (EmulationMenuSettings.showOverlay) {
+ if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
addOverlayControls(layout)
}
invalidate()
}
- private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
+ private fun saveControlPosition(id: String, x: Int, y: Int, layout: OverlayLayout) {
val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
val min = windowSize.first
val max = windowSize.second
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
- .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
- .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
- .apply()
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ val data = overlayControlData.firstOrNull { it.id == id }
+ val newPosition = Pair((x - min.x).toDouble() / max.x, (y - min.y).toDouble() / max.y)
+ when (layout) {
+ OverlayLayout.Landscape -> data?.landscapePosition = newPosition
+ OverlayLayout.Portrait -> data?.portraitPosition = newPosition
+ OverlayLayout.Foldable -> data?.foldablePosition = newPosition
+ }
+ NativeConfig.setOverlayControlData(overlayControlData)
}
fun setIsInEditMode(editMode: Boolean) {
inEditMode = editMode
}
- private fun resetCurrentLayout() {
- defaultOverlayByLayout(layout)
- val layoutIndex = overlayLayouts.indexOf(layout)
- preferences.edit()
- .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
- .apply()
+ /**
+ * Applies and saves all default values for the overlay
+ */
+ private fun populateDefaultConfig() {
+ val newConfig = OverlayControl.entries.map { it.toOverlayControlData() }
+ NativeConfig.setOverlayControlData(newConfig.toTypedArray())
+ NativeConfig.saveGlobalConfig()
}
- private fun resetAllLayouts() {
- val editor = preferences.edit()
- overlayLayouts.forEachIndexed { i, layout ->
- defaultOverlayByLayout(layout)
- editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i])
+ /**
+ * Checks if any new controls were added to OverlayControl that do not exist within deserialized
+ * config and adds / saves them if necessary
+ *
+ * @param overlayControlData Overlay control data from [NativeConfig.getOverlayControlData]
+ */
+ private fun checkForNewControls(overlayControlData: Array<OverlayControlData>) {
+ val missingControls = mutableListOf<OverlayControlData>()
+ OverlayControl.entries.forEach { defaultControl ->
+ val controlData = overlayControlData.firstOrNull { it.id == defaultControl.id }
+ if (controlData == null) {
+ missingControls.add(defaultControl.toOverlayControlData())
+ }
+ }
+
+ if (missingControls.isNotEmpty()) {
+ NativeConfig.setOverlayControlData(
+ arrayOf(*overlayControlData, *(missingControls.toTypedArray()))
+ )
+ NativeConfig.saveGlobalConfig()
}
- editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
- editor.apply()
}
fun resetLayoutVisibilityAndPlacement() {
- defaultOverlayByLayout(layout)
- val editor = preferences.edit()
- Settings.overlayPreferences.forEachIndexed { _, pref ->
- editor.remove(pref)
+ defaultOverlayPositionByLayout(layout)
+
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ overlayControlData.forEach {
+ it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
}
- editor.apply()
+ NativeConfig.setOverlayControlData(overlayControlData)
+
refreshControls()
}
- private val landscapeResources = arrayOf(
- R.integer.SWITCH_BUTTON_A_X,
- R.integer.SWITCH_BUTTON_A_Y,
- R.integer.SWITCH_BUTTON_B_X,
- R.integer.SWITCH_BUTTON_B_Y,
- R.integer.SWITCH_BUTTON_X_X,
- R.integer.SWITCH_BUTTON_X_Y,
- R.integer.SWITCH_BUTTON_Y_X,
- R.integer.SWITCH_BUTTON_Y_Y,
- R.integer.SWITCH_TRIGGER_ZL_X,
- R.integer.SWITCH_TRIGGER_ZL_Y,
- R.integer.SWITCH_TRIGGER_ZR_X,
- R.integer.SWITCH_TRIGGER_ZR_Y,
- R.integer.SWITCH_BUTTON_DPAD_X,
- R.integer.SWITCH_BUTTON_DPAD_Y,
- R.integer.SWITCH_TRIGGER_L_X,
- R.integer.SWITCH_TRIGGER_L_Y,
- R.integer.SWITCH_TRIGGER_R_X,
- R.integer.SWITCH_TRIGGER_R_Y,
- R.integer.SWITCH_BUTTON_PLUS_X,
- R.integer.SWITCH_BUTTON_PLUS_Y,
- R.integer.SWITCH_BUTTON_MINUS_X,
- R.integer.SWITCH_BUTTON_MINUS_Y,
- R.integer.SWITCH_BUTTON_HOME_X,
- R.integer.SWITCH_BUTTON_HOME_Y,
- R.integer.SWITCH_BUTTON_CAPTURE_X,
- R.integer.SWITCH_BUTTON_CAPTURE_Y,
- R.integer.SWITCH_STICK_R_X,
- R.integer.SWITCH_STICK_R_Y,
- R.integer.SWITCH_STICK_L_X,
- R.integer.SWITCH_STICK_L_Y,
- R.integer.SWITCH_BUTTON_STICK_L_X,
- R.integer.SWITCH_BUTTON_STICK_L_Y,
- R.integer.SWITCH_BUTTON_STICK_R_X,
- R.integer.SWITCH_BUTTON_STICK_R_Y
- )
-
- private val portraitResources = arrayOf(
- R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
- R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
- R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
- R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
- R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
- R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
- R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
- R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
- R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
- R.integer.SWITCH_STICK_R_X_PORTRAIT,
- R.integer.SWITCH_STICK_R_Y_PORTRAIT,
- R.integer.SWITCH_STICK_L_X_PORTRAIT,
- R.integer.SWITCH_STICK_L_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
- R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
- R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
- )
-
- private val foldableResources = arrayOf(
- R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
- R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
- R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
- R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
- R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
- R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
- R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
- R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
- R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
- R.integer.SWITCH_STICK_R_X_FOLDABLE,
- R.integer.SWITCH_STICK_R_Y_FOLDABLE,
- R.integer.SWITCH_STICK_L_X_FOLDABLE,
- R.integer.SWITCH_STICK_L_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
- R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
- R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
- )
-
- private fun getResourceValue(layout: String, position: Int): Float {
- return when (layout) {
- PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
- FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
- else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
+ private fun defaultOverlayPositionByLayout(layout: OverlayLayout) {
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ for (data in overlayControlData) {
+ val defaultControlData = OverlayControl.from(data.id) ?: continue
+ val position = defaultControlData.getDefaultPositionForLayout(layout)
+ when (layout) {
+ OverlayLayout.Landscape -> data.landscapePosition = position
+ OverlayLayout.Portrait -> data.portraitPosition = position
+ OverlayLayout.Foldable -> data.foldablePosition = position
+ }
}
- }
-
- private fun defaultOverlayByLayout(layout: String) {
- // Each value represents the position of the button in relation to the screen size without insets.
- preferences.edit()
- .putFloat(
- "${Settings.PREF_BUTTON_A}-X$layout",
- getResourceValue(layout, 0)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_A}-Y$layout",
- getResourceValue(layout, 1)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_B}-X$layout",
- getResourceValue(layout, 2)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_B}-Y$layout",
- getResourceValue(layout, 3)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_X}-X$layout",
- getResourceValue(layout, 4)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_X}-Y$layout",
- getResourceValue(layout, 5)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_Y}-X$layout",
- getResourceValue(layout, 6)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_Y}-Y$layout",
- getResourceValue(layout, 7)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_ZL}-X$layout",
- getResourceValue(layout, 8)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_ZL}-Y$layout",
- getResourceValue(layout, 9)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_ZR}-X$layout",
- getResourceValue(layout, 10)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_ZR}-Y$layout",
- getResourceValue(layout, 11)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_DPAD}-X$layout",
- getResourceValue(layout, 12)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_DPAD}-Y$layout",
- getResourceValue(layout, 13)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_L}-X$layout",
- getResourceValue(layout, 14)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_L}-Y$layout",
- getResourceValue(layout, 15)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_R}-X$layout",
- getResourceValue(layout, 16)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_R}-Y$layout",
- getResourceValue(layout, 17)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_PLUS}-X$layout",
- getResourceValue(layout, 18)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_PLUS}-Y$layout",
- getResourceValue(layout, 19)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_MINUS}-X$layout",
- getResourceValue(layout, 20)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_MINUS}-Y$layout",
- getResourceValue(layout, 21)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_HOME}-X$layout",
- getResourceValue(layout, 22)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_HOME}-Y$layout",
- getResourceValue(layout, 23)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
- getResourceValue(layout, 24)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
- getResourceValue(layout, 25)
- )
- .putFloat(
- "${Settings.PREF_STICK_R}-X$layout",
- getResourceValue(layout, 26)
- )
- .putFloat(
- "${Settings.PREF_STICK_R}-Y$layout",
- getResourceValue(layout, 27)
- )
- .putFloat(
- "${Settings.PREF_STICK_L}-X$layout",
- getResourceValue(layout, 28)
- )
- .putFloat(
- "${Settings.PREF_STICK_L}-Y$layout",
- getResourceValue(layout, 29)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_STICK_L}-X$layout",
- getResourceValue(layout, 30)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
- getResourceValue(layout, 31)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_STICK_R}-X$layout",
- getResourceValue(layout, 32)
- )
- .putFloat(
- "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
- getResourceValue(layout, 33)
- )
- .apply()
+ NativeConfig.setOverlayControlData(overlayControlData)
}
override fun isInEditMode(): Boolean {
@@ -913,18 +704,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
FOLDABLE_OVERLAY_VERSION
)
- private val preferences: SharedPreferences =
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
- const val LANDSCAPE = "_Landscape"
- const val PORTRAIT = "_Portrait"
- const val FOLDABLE = "_Foldable"
- val overlayLayouts = listOf(
- LANDSCAPE,
- PORTRAIT,
- FOLDABLE
- )
-
/**
* Resizes a [Bitmap] by a given scale factor
*
@@ -1036,29 +815,19 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
* In the input overlay configuration menu,
* once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay).
* the X and Y coordinates of the button at the END of its touch event
- * (when you remove your finger/stylus from the touchscreen) are then stored
- * within a SharedPreferences instance so that those values can be retrieved here.
- *
- *
- * This has a few benefits over the conventional way of storing the values
- * (ie. within the yuzu ini file).
- *
- * * No native calls
- * * Keeps Android-only values inside the Android environment
- *
- *
+ * (when you remove your finger/stylus from the touchscreen) are then stored in a native .
*
* Technically no modifications should need to be performed on the returned
* InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait
* for Android to call the onDraw method.
*
- * @param context The current [Context].
- * @param windowSize The size of the window to draw the overlay on.
- * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
- * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
- * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
- * @param prefId Identifier for determining where a button appears on screen.
- * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+ * @param context The current [Context].
+ * @param windowSize The size of the window to draw the overlay on.
+ * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
+ * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
+ * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
+ * @param overlayControlData Identifier for determining where a button appears on screen.
+ * @param position The position on screen as represented by an x and y value between 0 and 1.
* @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
*/
private fun initializeOverlayButton(
@@ -1067,33 +836,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultResId: Int,
pressedResId: Int,
buttonId: Int,
- prefId: String,
- layout: String
+ overlayControlData: OverlayControlData,
+ position: Pair<Double, Double>
): InputOverlayDrawableButton {
// Resources handle for fetching the initial Drawable resource.
val res = context.resources
- // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
- val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
// Decide scale based on button preference ID and user preference
- var scale: Float = when (prefId) {
- Settings.PREF_BUTTON_HOME,
- Settings.PREF_BUTTON_SCREENSHOT,
- Settings.PREF_BUTTON_PLUS,
- Settings.PREF_BUTTON_MINUS -> 0.07f
+ var scale: Float = when (overlayControlData.id) {
+ OverlayControl.BUTTON_HOME.id,
+ OverlayControl.BUTTON_CAPTURE.id,
+ OverlayControl.BUTTON_PLUS.id,
+ OverlayControl.BUTTON_MINUS.id -> 0.07f
- Settings.PREF_BUTTON_L,
- Settings.PREF_BUTTON_R,
- Settings.PREF_BUTTON_ZL,
- Settings.PREF_BUTTON_ZR -> 0.26f
+ OverlayControl.BUTTON_L.id,
+ OverlayControl.BUTTON_R.id,
+ OverlayControl.BUTTON_ZL.id,
+ OverlayControl.BUTTON_ZR.id -> 0.26f
- Settings.PREF_BUTTON_STICK_L,
- Settings.PREF_BUTTON_STICK_R -> 0.155f
+ OverlayControl.BUTTON_STICK_L.id,
+ OverlayControl.BUTTON_STICK_R.id -> 0.155f
else -> 0.11f
}
- scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+ scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Initialize the InputOverlayDrawableButton.
@@ -1104,7 +870,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultStateBitmap,
pressedStateBitmap,
buttonId,
- prefId
+ overlayControlData
)
// Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -1113,12 +879,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
- val xKey = "$prefId-X$layout"
- val yKey = "$prefId-Y$layout"
- val drawableXPercent = sPrefs.getFloat(xKey, 0f)
- val drawableYPercent = sPrefs.getFloat(yKey, 0f)
- val drawableX = (drawableXPercent * max.x + min.x).toInt()
- val drawableY = (drawableYPercent * max.y + min.y).toInt()
+ val drawableX = (position.first * max.x + min.x).toInt()
+ val drawableY = (position.second * max.y + min.y).toInt()
val width = overlayDrawable.width
val height = overlayDrawable.height
@@ -1136,8 +898,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
drawableX - (width / 2),
drawableY - (height / 2)
)
- val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
- overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+ overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
return overlayDrawable
}
@@ -1149,7 +910,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
* @param defaultResId The [Bitmap] resource ID of the default state.
* @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
* @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
- * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+ * @param position The position on screen as represented by an x and y value between 0 and 1.
* @return The initialized [InputOverlayDrawableDpad]
*/
private fun initializeOverlayDpad(
@@ -1158,17 +919,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultResId: Int,
pressedOneDirectionResId: Int,
pressedTwoDirectionsResId: Int,
- layout: String
+ position: Pair<Double, Double>
): InputOverlayDrawableDpad {
// Resources handle for fetching the initial Drawable resource.
val res = context.resources
- // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad.
- val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
// Decide scale based on button ID and user preference
var scale = 0.25f
- scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+ scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Initialize the InputOverlayDrawableDpad.
@@ -1195,10 +953,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu.
- val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
- val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
- val drawableX = (drawableXPercent * max.x + min.x).toInt()
- val drawableY = (drawableYPercent * max.y + min.y).toInt()
+ val drawableX = (position.first * max.x + min.x).toInt()
+ val drawableY = (position.second * max.y + min.y).toInt()
val width = overlayDrawable.width
val height = overlayDrawable.height
@@ -1213,8 +969,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// Need to set the image's position
overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2))
- val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
- overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+ overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
return overlayDrawable
}
@@ -1227,9 +982,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
* @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around).
* @param pressedResInner Resource ID for the pressed inner image of the joystick.
* @param joystick Identifier for which joystick this is.
- * @param button Identifier for which joystick button this is.
- * @param prefId Identifier for determining where a button appears on screen.
- * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
+ * @param buttonId Identifier for which joystick button this is.
+ * @param overlayControlData Identifier for determining where a button appears on screen.
+ * @param position The position on screen as represented by an x and y value between 0 and 1.
* @return The initialized [InputOverlayDrawableJoystick].
*/
private fun initializeOverlayJoystick(
@@ -1239,19 +994,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultResInner: Int,
pressedResInner: Int,
joystick: Int,
- button: Int,
- prefId: String,
- layout: String
+ buttonId: Int,
+ overlayControlData: OverlayControlData,
+ position: Pair<Double, Double>
): InputOverlayDrawableJoystick {
// Resources handle for fetching the initial Drawable resource.
val res = context.resources
- // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick.
- val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
// Decide scale based on user preference
var scale = 0.3f
- scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat()
+ scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
scale /= 100f
// Initialize the InputOverlayDrawableJoystick.
@@ -1265,10 +1017,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
- val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
- val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
- val drawableX = (drawableXPercent * max.x + min.x).toInt()
- val drawableY = (drawableYPercent * max.y + min.y).toInt()
+ val drawableX = (position.first * max.x + min.x).toInt()
+ val drawableY = (position.second * max.y + min.y).toInt()
val outerScale = 1.66f
// Now set the bounds for the InputOverlayDrawableJoystick.
@@ -1292,14 +1042,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
outerRect,
innerRect,
joystick,
- button,
- prefId
+ buttonId,
+ overlayControlData.id
)
// Need to set the image's position
overlayDrawable.setPosition(drawableX, drawableY)
- val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100)
- overlayDrawable.setOpacity(savedOpacity * 255 / 100)
+ overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
return overlayDrawable
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index 2c28dda88..b14a4f96e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -10,6 +10,7 @@ import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
/**
* Custom [BitmapDrawable] that is capable
@@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
defaultStateBitmap: Bitmap,
pressedStateBitmap: Bitmap,
val buttonId: Int,
- val prefId: String
+ val overlayControlData: OverlayControlData
) {
// The ID value what motion event is tracking
var trackId: Int
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 518b1e783..113bf7c24 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -14,7 +14,7 @@ import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
/**
* Custom [BitmapDrawable] that is capable
@@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
pressedState = true
outerBitmap.alpha = 0
boundsBoxBitmap.alpha = opacity
- if (EmulationMenuSettings.joystickRelCenter) {
+ if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
virtBounds.offset(
xPosition - virtBounds.centerX(),
yPosition - virtBounds.centerY()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
new file mode 100644
index 000000000..a0eeadf4b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
@@ -0,0 +1,188 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+import androidx.annotation.IntegerRes
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+
+enum class OverlayControl(
+ val id: String,
+ val defaultVisibility: Boolean,
+ @IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
+ @IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
+ @IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
+) {
+ BUTTON_A(
+ "button_a",
+ true,
+ Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
+ Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
+ ),
+ BUTTON_B(
+ "button_b",
+ true,
+ Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
+ Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
+ ),
+ BUTTON_X(
+ "button_x",
+ true,
+ Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
+ Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
+ ),
+ BUTTON_Y(
+ "button_y",
+ true,
+ Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
+ Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
+ ),
+ BUTTON_PLUS(
+ "button_plus",
+ true,
+ Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
+ Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
+ ),
+ BUTTON_MINUS(
+ "button_minus",
+ true,
+ Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
+ Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
+ ),
+ BUTTON_HOME(
+ "button_home",
+ false,
+ Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
+ Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
+ ),
+ BUTTON_CAPTURE(
+ "button_capture",
+ false,
+ Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
+ Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
+ ),
+ BUTTON_L(
+ "button_l",
+ true,
+ Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
+ Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
+ ),
+ BUTTON_R(
+ "button_r",
+ true,
+ Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
+ Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
+ ),
+ BUTTON_ZL(
+ "button_zl",
+ true,
+ Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
+ Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
+ ),
+ BUTTON_ZR(
+ "button_zr",
+ true,
+ Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
+ Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
+ ),
+ BUTTON_STICK_L(
+ "button_stick_l",
+ true,
+ Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
+ Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
+ ),
+ BUTTON_STICK_R(
+ "button_stick_r",
+ true,
+ Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
+ Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
+ Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
+ ),
+ STICK_L(
+ "stick_l",
+ true,
+ Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
+ Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
+ Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
+ ),
+ STICK_R(
+ "stick_r",
+ true,
+ Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
+ Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
+ Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
+ ),
+ COMBINED_DPAD(
+ "combined_dpad",
+ true,
+ Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
+ Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
+ Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
+ );
+
+ fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
+ val rawResourcePair: Pair<Int, Int>
+ YuzuApplication.appContext.resources.apply {
+ rawResourcePair = when (layout) {
+ OverlayLayout.Landscape -> {
+ Pair(
+ getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
+ getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
+ )
+ }
+
+ OverlayLayout.Portrait -> {
+ Pair(
+ getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
+ getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
+ )
+ }
+
+ OverlayLayout.Foldable -> {
+ Pair(
+ getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
+ getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
+ )
+ }
+ }
+ }
+
+ return Pair(
+ rawResourcePair.first.toDouble() / 1000,
+ rawResourcePair.second.toDouble() / 1000
+ )
+ }
+
+ fun toOverlayControlData(): OverlayControlData =
+ OverlayControlData(
+ id,
+ defaultVisibility,
+ getDefaultPositionForLayout(OverlayLayout.Landscape),
+ getDefaultPositionForLayout(OverlayLayout.Portrait),
+ getDefaultPositionForLayout(OverlayLayout.Foldable)
+ )
+
+ companion object {
+ val map: HashMap<String, OverlayControl> by lazy {
+ val hashMap = hashMapOf<String, OverlayControl>()
+ entries.forEach { hashMap[it.id] = it }
+ hashMap
+ }
+
+ fun from(id: String): OverlayControl? = map[id]
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
new file mode 100644
index 000000000..26cfeb1db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+data class OverlayControlData(
+ val id: String,
+ var enabled: Boolean,
+ var landscapePosition: Pair<Double, Double>,
+ var portraitPosition: Pair<Double, Double>,
+ var foldablePosition: Pair<Double, Double>
+) {
+ fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
+ when (layout) {
+ OverlayLayout.Landscape -> landscapePosition
+ OverlayLayout.Portrait -> portraitPosition
+ OverlayLayout.Foldable -> foldablePosition
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
new file mode 100644
index 000000000..6bd74c82f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+import androidx.annotation.IntegerRes
+
+data class OverlayControlDefault(
+ val buttonId: String,
+ @IntegerRes val landscapePositionResource: Pair<Int, Int>,
+ @IntegerRes val portraitPositionResource: Pair<Int, Int>,
+ @IntegerRes val foldablePositionResource: Pair<Int, Int>
+)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
new file mode 100644
index 000000000..d728164e5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay.model
+
+enum class OverlayLayout(val id: String) {
+ Landscape("Landscape"),
+ Portrait("Portrait"),
+ Foldable("Foldable")
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index fc0eeb9ad..54380323e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -91,18 +91,20 @@ class GamesFragment : Fragment() {
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it }
+ gamesViewModel.isReloading.collect {
+ binding.swipeRefresh.isRefreshing = it
+ if (gamesViewModel.games.value.isEmpty() && !it) {
+ binding.noticeText.visibility = View.VISIBLE
+ } else {
+ binding.noticeText.visibility = View.INVISIBLE
+ }
+ }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.games.collectLatest {
(binding.gridGames.adapter as GameAdapter).submitList(it)
- if (it.isEmpty()) {
- binding.noticeText.visibility = View.VISIBLE
- } else {
- binding.noticeText.visibility = View.GONE
- }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index b4117d761..622ae996e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -625,7 +625,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
File(DirectoryInitialization.userDirectory!!),
DirectoryInitialization.userDirectory!!,
BufferedOutputStream(contentResolver.openOutputStream(result)),
- taskViewModel.cancelled
+ taskViewModel.cancelled,
+ compression = false
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.user_data_export_success)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 0197fd712..de0794a17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -3,9 +3,17 @@
package org.yuzu.yuzu_emu.utils
+import androidx.preference.PreferenceManager
import java.io.IOException
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.IntSetting
+import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
+import org.yuzu.yuzu_emu.overlay.model.OverlayControl
+import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
+import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
object DirectoryInitialization {
private var userPath: String? = null
@@ -17,6 +25,7 @@ object DirectoryInitialization {
initializeInternalStorage()
NativeLibrary.initializeSystem(false)
NativeConfig.initializeGlobalConfig()
+ migrateSettings()
areDirectoriesReady = true
}
}
@@ -35,4 +44,170 @@ object DirectoryInitialization {
e.printStackTrace()
}
}
+
+ private fun migrateSettings() {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ var saveConfig = false
+ val theme = preferences.migratePreference<Int>(Settings.PREF_THEME)
+ if (theme != null) {
+ IntSetting.THEME.setInt(theme)
+ saveConfig = true
+ }
+
+ val themeMode = preferences.migratePreference<Int>(Settings.PREF_THEME_MODE)
+ if (themeMode != null) {
+ IntSetting.THEME_MODE.setInt(themeMode)
+ saveConfig = true
+ }
+
+ val blackBackgrounds =
+ preferences.migratePreference<Boolean>(Settings.PREF_BLACK_BACKGROUNDS)
+ if (blackBackgrounds != null) {
+ BooleanSetting.BLACK_BACKGROUNDS.setBoolean(blackBackgrounds)
+ saveConfig = true
+ }
+
+ val joystickRelCenter =
+ preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
+ if (joystickRelCenter != null) {
+ BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
+ saveConfig = true
+ }
+
+ val dpadSlide =
+ preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
+ if (dpadSlide != null) {
+ BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
+ saveConfig = true
+ }
+
+ val hapticFeedback =
+ preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
+ if (hapticFeedback != null) {
+ BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
+ saveConfig = true
+ }
+
+ val showPerformanceOverlay =
+ preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
+ if (showPerformanceOverlay != null) {
+ BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
+ saveConfig = true
+ }
+
+ val showInputOverlay =
+ preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
+ if (showInputOverlay != null) {
+ BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
+ saveConfig = true
+ }
+
+ val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
+ if (overlayOpacity != null) {
+ IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
+ saveConfig = true
+ }
+
+ val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
+ if (overlayScale != null) {
+ IntSetting.OVERLAY_SCALE.setInt(overlayScale)
+ saveConfig = true
+ }
+
+ var setOverlayData = false
+ val overlayControlData = NativeConfig.getOverlayControlData()
+ if (overlayControlData.isEmpty()) {
+ val overlayControlDataMap =
+ NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
+ for (button in Settings.overlayPreferences) {
+ val buttonId = convertButtonId(button)
+ var buttonEnabled = preferences.migratePreference<Boolean>(button)
+ if (buttonEnabled == null) {
+ buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
+ }
+
+ var landscapeXPosition = preferences.migratePreference<Float>(
+ "$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
+ )?.toDouble()
+ var landscapeYPosition = preferences.migratePreference<Float>(
+ "$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
+ )?.toDouble()
+ if (landscapeXPosition == null || landscapeYPosition == null) {
+ val landscapePosition = OverlayControl.map[buttonId]
+ ?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
+ landscapeXPosition = landscapePosition.first
+ landscapeYPosition = landscapePosition.second
+ }
+
+ var portraitXPosition = preferences.migratePreference<Float>(
+ "$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
+ )?.toDouble()
+ var portraitYPosition = preferences.migratePreference<Float>(
+ "$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
+ )?.toDouble()
+ if (portraitXPosition == null || portraitYPosition == null) {
+ val portraitPosition = OverlayControl.map[buttonId]
+ ?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
+ portraitXPosition = portraitPosition.first
+ portraitYPosition = portraitPosition.second
+ }
+
+ var foldableXPosition = preferences.migratePreference<Float>(
+ "$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
+ )?.toDouble()
+ var foldableYPosition = preferences.migratePreference<Float>(
+ "$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
+ )?.toDouble()
+ if (foldableXPosition == null || foldableYPosition == null) {
+ val foldablePosition = OverlayControl.map[buttonId]
+ ?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
+ foldableXPosition = foldablePosition.first
+ foldableYPosition = foldablePosition.second
+ }
+
+ val controlData = OverlayControlData(
+ buttonId,
+ buttonEnabled,
+ Pair(landscapeXPosition, landscapeYPosition),
+ Pair(portraitXPosition, portraitYPosition),
+ Pair(foldableXPosition, foldableYPosition)
+ )
+ overlayControlDataMap[buttonId] = controlData
+ setOverlayData = true
+ }
+
+ if (setOverlayData) {
+ NativeConfig.setOverlayControlData(
+ overlayControlDataMap.map { it.value }.toTypedArray()
+ )
+ saveConfig = true
+ }
+ }
+
+ if (saveConfig) {
+ NativeConfig.saveGlobalConfig()
+ }
+ }
+
+ private fun convertButtonId(buttonId: String): String =
+ when (buttonId) {
+ Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
+ Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
+ Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
+ Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
+ Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
+ Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
+ Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
+ Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
+ Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
+ Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
+ Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
+ Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
+ Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
+ Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
+ Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
+ Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
+ Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
+ else -> ""
+ }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
deleted file mode 100644
index 7e8f058c1..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.utils
-
-import androidx.preference.PreferenceManager
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.model.Settings
-
-object EmulationMenuSettings {
- private val preferences =
- PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
- var joystickRelCenter: Boolean
- get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
- set(value) {
- preferences.edit()
- .putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
- .apply()
- }
- var dpadSlide: Boolean
- get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
- set(value) {
- preferences.edit()
- .putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
- .apply()
- }
- var hapticFeedback: Boolean
- get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
- set(value) {
- preferences.edit()
- .putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
- .apply()
- }
-
- var showFps: Boolean
- get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
- set(value) {
- preferences.edit()
- .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
- .apply()
- }
- var showOverlay: Boolean
- get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
- set(value) {
- preferences.edit()
- .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
- .apply()
- }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 00c6bf90e..132f002fb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
+import java.util.zip.Deflater
import java.util.zip.ZipOutputStream
import kotlin.IllegalStateException
@@ -312,15 +313,23 @@ object FileUtil {
* @param inputFile File representation of the item that will be zipped
* @param rootDir Directory containing the inputFile
* @param outputStream Stream where the zip file will be output
+ * @param cancelled [StateFlow] that reports whether this process has been cancelled
+ * @param compression Disables compression if true
*/
fun zipFromInternalStorage(
inputFile: File,
rootDir: String,
outputStream: BufferedOutputStream,
- cancelled: StateFlow<Boolean>? = null
+ cancelled: StateFlow<Boolean>? = null,
+ compression: Boolean = true
): TaskState {
try {
ZipOutputStream(outputStream).use { zos ->
+ if (!compression) {
+ zos.setMethod(ZipOutputStream.DEFLATED)
+ zos.setLevel(Deflater.NO_COMPRESSION)
+ }
+
inputFile.walkTopDown().forEach { file ->
if (cancelled?.value == true) {
return TaskState.Cancelled
@@ -338,6 +347,7 @@ object FileUtil {
}
}
} catch (e: Exception) {
+ Log.error("[FileUtil] Failed creating zip file - ${e.message}")
return TaskState.Failed
}
return TaskState.Completed
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index 7512d5eed..a4c14b3a7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir
+import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
object NativeConfig {
/**
@@ -150,4 +151,21 @@ object NativeConfig {
*/
@Synchronized
external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
+
+ /**
+ * Gets an array of [OverlayControlData] from settings
+ *
+ * @return An array of [OverlayControlData]
+ */
+ @Synchronized
+ external fun getOverlayControlData(): Array<OverlayControlData>
+
+ /**
+ * Clears the AndroidSettings::values.overlay_control_data array and replaces its values
+ * with [overlayControlData]
+ *
+ * @param overlayControlData Replacement array of [OverlayControlData]
+ */
+ @Synchronized
+ external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
new file mode 100644
index 000000000..a233ba25c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.content.SharedPreferences
+
+object PreferenceUtil {
+ /**
+ * Retrieves a shared preference value and then deletes the value in storage.
+ * @param key Associated key for the value in this preferences instance
+ * @return Typed value associated with [key]. Null if no such key exists.
+ */
+ inline fun <reified T> SharedPreferences.migratePreference(key: String): T? {
+ if (!this.contains(key)) {
+ return null
+ }
+
+ val value: Any = when (T::class) {
+ String::class -> this.getString(key, "")!!
+
+ Boolean::class -> this.getBoolean(key, false)
+
+ Int::class -> this.getInt(key, 0)
+
+ Float::class -> this.getFloat(key, 0f)
+
+ Long::class -> this.getLong(key, 0)
+
+ else -> throw IllegalStateException("Tried to migrate preference with invalid type!")
+ }
+ deletePreference(key)
+ return value as T
+ }
+
+ fun SharedPreferences.deletePreference(key: String) = this.edit().remove(key).apply()
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index f312e24cf..6f7f40e43 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -5,38 +5,38 @@ package org.yuzu.yuzu_emu.utils
import android.content.res.Configuration
import android.graphics.Color
+import android.os.Build
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
-import androidx.preference.PreferenceManager
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R
-import org.yuzu.yuzu_emu.YuzuApplication
-import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
+import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
- private const val DEFAULT = 0
- private const val MATERIAL_YOU = 1
-
fun setTheme(activity: AppCompatActivity) {
- val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
setThemeMode(activity)
- when (preferences.getInt(Settings.PREF_THEME, 0)) {
- DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main)
- MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
+ when (Theme.from(IntSetting.THEME.getInt())) {
+ Theme.Default -> activity.setTheme(R.style.Theme_Yuzu_Main)
+ Theme.MaterialYou -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
+ } else {
+ activity.setTheme(R.style.Theme_Yuzu_Main)
+ }
+ }
}
// Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds.
- if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
- isNightMode(activity)
- ) {
+ if (BooleanSetting.BLACK_BACKGROUNDS.getBoolean() && isNightMode(activity)) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
}
}
@@ -60,8 +60,7 @@ object ThemeHelper {
}
fun setThemeMode(activity: AppCompatActivity) {
- val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
- .getInt(Settings.PREF_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
+ val themeMode = IntSetting.THEME_MODE.getInt()
activity.delegate.localNightMode = themeMode
val windowController = WindowCompat.getInsetsController(
activity.window,
@@ -95,3 +94,12 @@ object ThemeHelper {
windowController.isAppearanceLightNavigationBars = false
}
}
+
+enum class Theme(val int: Int) {
+ Default(0),
+ MaterialYou(1);
+
+ companion object {
+ fun from(int: Int): Theme = entries.firstOrNull { it.int == int } ?: Default
+ }
+}
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp
index 52d8ecfeb..1e884ffdd 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/android/app/src/main/jni/android_common/android_common.cpp
@@ -9,6 +9,7 @@
#include <jni.h>
#include "common/string_util.h"
+#include "jni/id_cache.h"
std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) {
@@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
jstring ToJString(JNIEnv* env, std::u16string_view str) {
return ToJString(env, Common::UTF16ToUTF8(str));
}
+
+double GetJDouble(JNIEnv* env, jobject jdouble) {
+ return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
+}
+
+jobject ToJDouble(JNIEnv* env, double value) {
+ return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
+}
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h
index ccb0c06f7..8eb803e1b 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/android/app/src/main/jni/android_common/android_common.h
@@ -10,3 +10,6 @@
std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_view str);
+
+double GetJDouble(JNIEnv* env, jobject jdouble);
+jobject ToJDouble(JNIEnv* env, double value);
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index 9c3a5a9b2..08aed3216 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -14,12 +14,6 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
}
}
-AndroidConfig::~AndroidConfig() {
- if (global) {
- AndroidConfig::SaveAllValues();
- }
-}
-
void AndroidConfig::ReloadAllValues() {
Reload();
ReadAndroidValues();
@@ -35,6 +29,7 @@ void AndroidConfig::ReadAndroidValues() {
if (global) {
ReadAndroidUIValues();
ReadUIValues();
+ ReadOverlayValues();
}
ReadDriverValues();
}
@@ -81,10 +76,42 @@ void AndroidConfig::ReadDriverValues() {
EndGroup();
}
+void AndroidConfig::ReadOverlayValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
+
+ ReadCategory(Settings::Category::Overlay);
+
+ AndroidSettings::values.overlay_control_data.clear();
+ const int control_data_size = BeginArray("control_data");
+ for (int i = 0; i < control_data_size; ++i) {
+ SetArrayIndex(i);
+ AndroidSettings::OverlayControlData control_data;
+ control_data.id = ReadStringSetting(std::string("id"));
+ control_data.enabled = ReadBooleanSetting(std::string("enabled"));
+ control_data.landscape_position.first =
+ ReadDoubleSetting(std::string("landscape\\x_position"));
+ control_data.landscape_position.second =
+ ReadDoubleSetting(std::string("landscape\\y_position"));
+ control_data.portrait_position.first =
+ ReadDoubleSetting(std::string("portrait\\x_position"));
+ control_data.portrait_position.second =
+ ReadDoubleSetting(std::string("portrait\\y_position"));
+ control_data.foldable_position.first =
+ ReadDoubleSetting(std::string("foldable\\x_position"));
+ control_data.foldable_position.second =
+ ReadDoubleSetting(std::string("foldable\\y_position"));
+ AndroidSettings::values.overlay_control_data.push_back(control_data);
+ }
+ EndArray();
+
+ EndGroup();
+}
+
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
SaveUIValues();
+ SaveOverlayValues();
}
SaveDriverValues();
@@ -114,8 +141,9 @@ void AndroidConfig::SavePathValues() {
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = AndroidSettings::values.game_dirs[i];
- WriteSetting(std::string("path"), game_dir.path);
- WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
+ WriteStringSetting(std::string("path"), game_dir.path);
+ WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
+ std::make_optional(false));
}
EndArray();
@@ -130,6 +158,35 @@ void AndroidConfig::SaveDriverValues() {
EndGroup();
}
+void AndroidConfig::SaveOverlayValues() {
+ BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
+
+ WriteCategory(Settings::Category::Overlay);
+
+ BeginArray("control_data");
+ for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
+ SetArrayIndex(i);
+ const auto& control_data = AndroidSettings::values.overlay_control_data[i];
+ WriteStringSetting(std::string("id"), control_data.id);
+ WriteBooleanSetting(std::string("enabled"), control_data.enabled);
+ WriteDoubleSetting(std::string("landscape\\x_position"),
+ control_data.landscape_position.first);
+ WriteDoubleSetting(std::string("landscape\\y_position"),
+ control_data.landscape_position.second);
+ WriteDoubleSetting(std::string("portrait\\x_position"),
+ control_data.portrait_position.first);
+ WriteDoubleSetting(std::string("portrait\\y_position"),
+ control_data.portrait_position.second);
+ WriteDoubleSetting(std::string("foldable\\x_position"),
+ control_data.foldable_position.first);
+ WriteDoubleSetting(std::string("foldable\\y_position"),
+ control_data.foldable_position.second);
+ }
+ EndArray();
+
+ EndGroup();
+}
+
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 2c12874e1..693e1e3f0 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -9,7 +9,6 @@ class AndroidConfig final : public Config {
public:
explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
- ~AndroidConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;
@@ -18,6 +17,7 @@ protected:
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadDriverValues();
+ void ReadOverlayValues();
void ReadHidbusValues() override {}
void ReadDebugControlValues() override {}
void ReadPathValues() override;
@@ -30,6 +30,7 @@ protected:
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveDriverValues();
+ void SaveOverlayValues();
void SaveHidbusValues() override {}
void SaveDebugControlValues() override {}
void SavePathValues() override;
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 3733f5a3c..559ae83eb 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -14,6 +14,14 @@ struct GameDir {
bool deep_scan = false;
};
+struct OverlayControlData {
+ std::string id;
+ bool enabled;
+ std::pair<double, double> landscape_position;
+ std::pair<double, double> portrait_position;
+ std::pair<double, double> foldable_position;
+};
+
struct Values {
Settings::Linkage linkage;
@@ -33,6 +41,28 @@ struct Values {
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
Settings::Category::GpuDriver};
+
+ Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
+ Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
+ Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
+ Settings::Category::Android};
+
+ // Input/performance overlay settings
+ std::vector<OverlayControlData> overlay_control_data;
+ Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
+ Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
+ Settings::Category::Overlay};
+
+ Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
+ Settings::Category::Overlay};
+ Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
+ Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
+ Settings::Category::Overlay};
+ Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
+ Settings::Category::Overlay};
+ Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
+ Settings::Category::Overlay};
+ Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
};
extern Values values;
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index e7a86d3fd..c79ad7d76 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field;
static jfieldID s_pair_second_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_landscape_position_field;
+static jfieldID s_overlay_control_data_portrait_position_field;
+static jfieldID s_overlay_control_data_foldable_position_field;
+
+static jclass s_double_class;
+static jmethodID s_double_constructor;
+static jfieldID s_double_value_field;
+
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache {
@@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
return s_pair_second_field;
}
+jclass GetOverlayControlDataClass() {
+ return s_overlay_control_data_class;
+}
+
+jmethodID GetOverlayControlDataConstructor() {
+ return s_overlay_control_data_constructor;
+}
+
+jfieldID GetOverlayControlDataIdField() {
+ return s_overlay_control_data_id_field;
+}
+
+jfieldID GetOverlayControlDataEnabledField() {
+ return s_overlay_control_data_enabled_field;
+}
+
+jfieldID GetOverlayControlDataLandscapePositionField() {
+ return s_overlay_control_data_landscape_position_field;
+}
+
+jfieldID GetOverlayControlDataPortraitPositionField() {
+ return s_overlay_control_data_portrait_position_field;
+}
+
+jfieldID GetOverlayControlDataFoldablePositionField() {
+ return s_overlay_control_data_foldable_position_field;
+}
+
+jclass GetDoubleClass() {
+ return s_double_class;
+}
+
+jmethodID GetDoubleConstructor() {
+ return s_double_constructor;
+}
+
+jfieldID GetDoubleValueField() {
+ return s_double_value_field;
+}
+
} // namespace IDCache
#ifdef __cplusplus
@@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
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");
+ s_overlay_control_data_class =
+ reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
+ s_overlay_control_data_constructor =
+ env->GetMethodID(overlay_control_data_class, "<init>",
+ "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
+ s_overlay_control_data_id_field =
+ env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
+ s_overlay_control_data_enabled_field =
+ env->GetFieldID(overlay_control_data_class, "enabled", "Z");
+ s_overlay_control_data_landscape_position_field =
+ env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
+ s_overlay_control_data_portrait_position_field =
+ env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
+ s_overlay_control_data_foldable_position_field =
+ env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
+ env->DeleteLocalRef(overlay_control_data_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_field = env->GetFieldID(double_class, "value", "D");
+ env->DeleteLocalRef(double_class);
+
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
@@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
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_double_class);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 24030be42..784d1412f 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
jfieldID GetPairFirstField();
jfieldID GetPairSecondField();
+jclass GetOverlayControlDataClass();
+jmethodID GetOverlayControlDataConstructor();
+jfieldID GetOverlayControlDataIdField();
+jfieldID GetOverlayControlDataEnabledField();
+jfieldID GetOverlayControlDataLandscapePositionField();
+jfieldID GetOverlayControlDataPortraitPositionField();
+jfieldID GetOverlayControlDataFoldablePositionField();
+
+jclass GetDoubleClass();
+jmethodID GetDoubleConstructor();
+jfieldID GetDoubleValueField();
+
} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 324d9e9cd..535902483 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
Settings::values.disabled_addons[program_id] = disabled_addons;
}
+jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
+ jobject obj) {
+ jobjectArray joverlayControlDataArray =
+ env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
+ IDCache::GetOverlayControlDataClass(), nullptr);
+ for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
+ const auto& control_data = AndroidSettings::values.overlay_control_data[i];
+ jobject jlandscapePosition =
+ env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+ ToJDouble(env, control_data.landscape_position.first),
+ ToJDouble(env, control_data.landscape_position.second));
+ jobject jportraitPosition =
+ env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+ ToJDouble(env, control_data.portrait_position.first),
+ ToJDouble(env, control_data.portrait_position.second));
+ jobject jfoldablePosition =
+ env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
+ ToJDouble(env, control_data.foldable_position.first),
+ ToJDouble(env, control_data.foldable_position.second));
+
+ jobject jcontrolData = env->NewObject(
+ IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
+ ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
+ jportraitPosition, jfoldablePosition);
+ env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
+ }
+ return joverlayControlDataArray;
+}
+
+void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
+ JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
+ AndroidSettings::values.overlay_control_data.clear();
+ int size = env->GetArrayLength(joverlayControlDataArray);
+
+ if (size == 0) {
+ return;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
+ jstring jidString = static_cast<jstring>(
+ env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
+ bool enabled = static_cast<bool>(env->GetBooleanField(
+ joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
+
+ jobject jlandscapePosition = env->GetObjectField(
+ joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
+ std::pair<double, double> landscape_position = std::make_pair(
+ GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
+ GetJDouble(env,
+ env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
+
+ jobject jportraitPosition = env->GetObjectField(
+ joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
+ std::pair<double, double> portrait_position = std::make_pair(
+ GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
+ GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
+
+ jobject jfoldablePosition = env->GetObjectField(
+ joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
+ std::pair<double, double> foldable_position = std::make_pair(
+ GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
+ GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
+
+ AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
+ GetJString(env, jidString), enabled, landscape_position, portrait_position,
+ foldable_position});
+ }
+}
+
} // extern "C"
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
index 5cb84182e..1c08e2e1b 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
@@ -24,7 +24,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
- android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
android:orientation="vertical"
android:layout_weight="1">
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
index 4885b4f6f..363781652 100644
--- a/src/android/app/src/main/res/menu/menu_overlay_options.xml
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -39,6 +39,11 @@
android:checkable="true" />
<item
+ android:id="@+id/menu_touchscreen"
+ android:title="@string/touchscreen"
+ android:checkable="true" />
+
+ <item
android:id="@+id/menu_reset_overlay"
android:title="@string/emulation_touch_overlay_reset" />
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index e3915ef4f..0363ff3b6 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -118,15 +118,23 @@
</integer-array>
<string-array name="rendererScreenLayoutNames">
+ <item>@string/screen_layout_auto</item>
+ <item>@string/screen_layout_sensor_landscape</item>
<item>@string/screen_layout_landscape</item>
+ <item>@string/screen_layout_reverse_landscape</item>
+ <item>@string/screen_layout_sensor_portrait</item>
<item>@string/screen_layout_portrait</item>
- <item>@string/screen_layout_auto</item>
+ <item>@string/screen_layout_reverse_portrait</item>
</string-array>
<integer-array name="rendererScreenLayoutValues">
+ <item>0</item>
<item>5</item>
+ <item>1</item>
+ <item>2</item>
+ <item>6</item>
<item>4</item>
- <item>0</item>
+ <item>3</item>
</integer-array>
<string-array name="rendererAspectRatioNames">
@@ -212,19 +220,19 @@
<item>B</item>
<item>X</item>
<item>Y</item>
+ <item>+</item>
+ <item>-</item>
+ <item>@string/gamepad_home</item>
+ <item>@string/gamepad_screenshot</item>
<item>L</item>
<item>R</item>
<item>ZL</item>
<item>ZR</item>
- <item>+</item>
- <item>-</item>
- <item>@string/gamepad_d_pad</item>
<item>@string/gamepad_left_stick</item>
<item>@string/gamepad_right_stick</item>
<item>L3</item>
<item>R3</item>
- <item>@string/gamepad_home</item>
- <item>@string/gamepad_screenshot</item>
+ <item>@string/gamepad_d_pad</item>
</string-array>
<string-array name="themeEntries">
@@ -267,4 +275,21 @@
<item>3</item>
</integer-array>
+ <string-array name="anisoEntries">
+ <item>@string/auto</item>
+ <item>@string/slider_default</item>
+ <item>@string/multiplier_two</item>
+ <item>@string/multiplier_four</item>
+ <item>@string/multiplier_eight</item>
+ <item>@string/multiplier_sixteen</item>
+ </string-array>
+ <integer-array name="anisoValues">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>4</item>
+ <item>5</item>
+ </integer-array>
+
</resources>
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index dc527965c..1c6f5db93 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -3,111 +3,111 @@
<integer name="grid_columns">1</integer>
<!-- Default SWITCH landscape layout -->
- <integer name="SWITCH_BUTTON_A_X">760</integer>
- <integer name="SWITCH_BUTTON_A_Y">790</integer>
- <integer name="SWITCH_BUTTON_B_X">710</integer>
- <integer name="SWITCH_BUTTON_B_Y">900</integer>
- <integer name="SWITCH_BUTTON_X_X">710</integer>
- <integer name="SWITCH_BUTTON_X_Y">680</integer>
- <integer name="SWITCH_BUTTON_Y_X">660</integer>
- <integer name="SWITCH_BUTTON_Y_Y">790</integer>
- <integer name="SWITCH_STICK_L_X">100</integer>
- <integer name="SWITCH_STICK_L_Y">670</integer>
- <integer name="SWITCH_STICK_R_X">900</integer>
- <integer name="SWITCH_STICK_R_Y">670</integer>
- <integer name="SWITCH_TRIGGER_L_X">70</integer>
- <integer name="SWITCH_TRIGGER_L_Y">220</integer>
- <integer name="SWITCH_TRIGGER_R_X">930</integer>
- <integer name="SWITCH_TRIGGER_R_Y">220</integer>
- <integer name="SWITCH_TRIGGER_ZL_X">70</integer>
- <integer name="SWITCH_TRIGGER_ZL_Y">90</integer>
- <integer name="SWITCH_TRIGGER_ZR_X">930</integer>
- <integer name="SWITCH_TRIGGER_ZR_Y">90</integer>
- <integer name="SWITCH_BUTTON_MINUS_X">460</integer>
- <integer name="SWITCH_BUTTON_MINUS_Y">950</integer>
- <integer name="SWITCH_BUTTON_PLUS_X">540</integer>
- <integer name="SWITCH_BUTTON_PLUS_Y">950</integer>
- <integer name="SWITCH_BUTTON_HOME_X">600</integer>
- <integer name="SWITCH_BUTTON_HOME_Y">950</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_X">400</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
- <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
- <integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
- <integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
- <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
- <integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
- <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
+ <integer name="BUTTON_A_X">760</integer>
+ <integer name="BUTTON_A_Y">790</integer>
+ <integer name="BUTTON_B_X">710</integer>
+ <integer name="BUTTON_B_Y">900</integer>
+ <integer name="BUTTON_X_X">710</integer>
+ <integer name="BUTTON_X_Y">680</integer>
+ <integer name="BUTTON_Y_X">660</integer>
+ <integer name="BUTTON_Y_Y">790</integer>
+ <integer name="BUTTON_PLUS_X">540</integer>
+ <integer name="BUTTON_PLUS_Y">950</integer>
+ <integer name="BUTTON_MINUS_X">460</integer>
+ <integer name="BUTTON_MINUS_Y">950</integer>
+ <integer name="BUTTON_HOME_X">600</integer>
+ <integer name="BUTTON_HOME_Y">950</integer>
+ <integer name="BUTTON_CAPTURE_X">400</integer>
+ <integer name="BUTTON_CAPTURE_Y">950</integer>
+ <integer name="BUTTON_L_X">70</integer>
+ <integer name="BUTTON_L_Y">220</integer>
+ <integer name="BUTTON_R_X">930</integer>
+ <integer name="BUTTON_R_Y">220</integer>
+ <integer name="BUTTON_ZL_X">70</integer>
+ <integer name="BUTTON_ZL_Y">90</integer>
+ <integer name="BUTTON_ZR_X">930</integer>
+ <integer name="BUTTON_ZR_Y">90</integer>
+ <integer name="BUTTON_STICK_L_X">870</integer>
+ <integer name="BUTTON_STICK_L_Y">400</integer>
+ <integer name="BUTTON_STICK_R_X">960</integer>
+ <integer name="BUTTON_STICK_R_Y">430</integer>
+ <integer name="STICK_L_X">100</integer>
+ <integer name="STICK_L_Y">670</integer>
+ <integer name="STICK_R_X">900</integer>
+ <integer name="STICK_R_Y">670</integer>
+ <integer name="COMBINED_DPAD_X">260</integer>
+ <integer name="COMBINED_DPAD_Y">790</integer>
<!-- Default SWITCH portrait layout -->
- <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
- <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer>
- <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer>
- <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer>
- <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer>
- <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer>
- <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer>
- <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer>
- <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer>
- <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer>
- <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer>
- <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer>
- <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer>
- <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer>
- <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer>
- <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer>
- <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer>
- <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer>
- <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer>
- <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer>
- <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer>
- <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer>
- <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer>
- <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer>
- <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer>
- <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
- <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
- <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
- <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
- <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
- <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
- <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
+ <integer name="BUTTON_A_X_PORTRAIT">840</integer>
+ <integer name="BUTTON_A_Y_PORTRAIT">840</integer>
+ <integer name="BUTTON_B_X_PORTRAIT">740</integer>
+ <integer name="BUTTON_B_Y_PORTRAIT">880</integer>
+ <integer name="BUTTON_X_X_PORTRAIT">740</integer>
+ <integer name="BUTTON_X_Y_PORTRAIT">800</integer>
+ <integer name="BUTTON_Y_X_PORTRAIT">640</integer>
+ <integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
+ <integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
+ <integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
+ <integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
+ <integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
+ <integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
+ <integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
+ <integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
+ <integer name="BUTTON_L_X_PORTRAIT">140</integer>
+ <integer name="BUTTON_L_Y_PORTRAIT">260</integer>
+ <integer name="BUTTON_R_X_PORTRAIT">860</integer>
+ <integer name="BUTTON_R_Y_PORTRAIT">260</integer>
+ <integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
+ <integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
+ <integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
+ <integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
+ <integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
+ <integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
+ <integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
+ <integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
+ <integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
+ <integer name="STICK_L_X_PORTRAIT">180</integer>
+ <integer name="STICK_L_Y_PORTRAIT">660</integer>
+ <integer name="STICK_R_X_PORTRAIT">820</integer>
+ <integer name="STICK_R_Y_PORTRAIT">660</integer>
+ <integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
+ <integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
<!-- Default SWITCH foldable layout -->
- <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
- <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer>
- <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer>
- <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer>
- <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer>
- <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer>
- <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer>
- <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer>
- <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer>
- <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer>
- <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer>
- <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer>
- <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer>
- <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer>
- <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer>
- <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer>
- <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer>
- <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer>
- <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer>
- <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer>
- <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer>
- <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer>
- <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer>
- <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer>
- <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer>
- <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer>
- <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
- <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
- <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
- <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
- <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
- <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
- <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
+ <integer name="BUTTON_A_X_FOLDABLE">840</integer>
+ <integer name="BUTTON_A_Y_FOLDABLE">390</integer>
+ <integer name="BUTTON_B_X_FOLDABLE">740</integer>
+ <integer name="BUTTON_B_Y_FOLDABLE">430</integer>
+ <integer name="BUTTON_X_X_FOLDABLE">740</integer>
+ <integer name="BUTTON_X_Y_FOLDABLE">350</integer>
+ <integer name="BUTTON_Y_X_FOLDABLE">640</integer>
+ <integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
+ <integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
+ <integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
+ <integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
+ <integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
+ <integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
+ <integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
+ <integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
+ <integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
+ <integer name="BUTTON_L_X_FOLDABLE">140</integer>
+ <integer name="BUTTON_L_Y_FOLDABLE">130</integer>
+ <integer name="BUTTON_R_X_FOLDABLE">860</integer>
+ <integer name="BUTTON_R_Y_FOLDABLE">130</integer>
+ <integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
+ <integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
+ <integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
+ <integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
+ <integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
+ <integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
+ <integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
+ <integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
+ <integer name="STICK_L_X_FOLDABLE">180</integer>
+ <integer name="STICK_L_Y_FOLDABLE">250</integer>
+ <integer name="STICK_R_X_FOLDABLE">820</integer>
+ <integer name="STICK_R_Y_FOLDABLE">250</integer>
+ <integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
+ <integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 0b80b04a4..83aa1b781 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -225,6 +225,8 @@
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="use_disk_shader_cache">Disk shader cache</string>
<string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
+ <string name="anisotropic_filtering">Anisotropic filtering</string>
+ <string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
<!-- Debug settings strings -->
<string name="cpu">CPU</string>
@@ -364,6 +366,7 @@
<string name="emulation_pause">Pause emulation</string>
<string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string>
+ <string name="touchscreen">Touchscreen</string>
<string name="load_settings">Loading settings…</string>
@@ -460,9 +463,13 @@
<string name="anti_aliasing_smaa">SMAA</string>
<!-- Screen Layouts -->
+ <string name="screen_layout_auto">Auto</string>
+ <string name="screen_layout_sensor_landscape">Sensor landscape</string>
<string name="screen_layout_landscape">Landscape</string>
+ <string name="screen_layout_reverse_landscape">Reverse landscape</string>
+ <string name="screen_layout_sensor_portrait">Sensor portrait</string>
<string name="screen_layout_portrait">Portrait</string>
- <string name="screen_layout_auto">Auto</string>
+ <string name="screen_layout_reverse_portrait">Reverse portrait</string>
<!-- Aspect Ratios -->
<string name="ratio_default">Default (16:9)</string>
@@ -506,6 +513,12 @@
<string name="oboe">oboe</string>
<string name="cubeb">cubeb</string>
+ <!-- Anisotropic filtering options -->
+ <string name="multiplier_two">2x</string>
+ <string name="multiplier_four">4x</string>
+ <string name="multiplier_eight">8x</string>
+ <string name="multiplier_sixteen">16x</string>
+
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts
index 51e559321..b77906ed6 100644
--- a/src/android/build.gradle.kts
+++ b/src/android/build.gradle.kts
@@ -5,7 +5,7 @@
plugins {
id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.1.2" apply false
- id("org.jetbrains.kotlin.android") version "1.8.21" apply false
+ id("org.jetbrains.kotlin.android") version "1.9.20" apply false
}
tasks.register("clean").configure {
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index c41d9d1ea..ee42ae529 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms};
DeviceSession::DeviceSession(Core::System& system_)
: system{system_}, thread_event{Core::Timing::CreateEvent(
"AudioOutSampleTick",
- [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) {
- return ThreadFunc();
- })} {}
+ [this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
DeviceSession::~DeviceSession() {
Finalize();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b58a7073f..8c57d47c6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,6 +64,8 @@ add_library(common STATIC
fs/path_util.cpp
fs/path_util.h
hash.h
+ heap_tracker.cpp
+ heap_tracker.h
hex_util.cpp
hex_util.h
host_memory.cpp
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index 6026b7dc2..e2c2cade3 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -3,16 +3,19 @@
#include "common/assert.h"
#include "common/common_funcs.h"
+#include "common/logging/backend.h"
#include "common/settings.h"
void assert_fail_impl() {
if (Settings::values.use_debug_asserts) {
+ Common::Log::Stop();
Crash();
}
}
[[noreturn]] void unreachable_impl() {
+ Common::Log::Stop();
Crash();
throw std::runtime_error("Unreachable code");
}
diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp
new file mode 100644
index 000000000..683208795
--- /dev/null
+++ b/src/common/heap_tracker.cpp
@@ -0,0 +1,281 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <fstream>
+#include <vector>
+
+#include "common/heap_tracker.h"
+#include "common/logging/log.h"
+
+namespace Common {
+
+namespace {
+
+s64 GetMaxPermissibleResidentMapCount() {
+ // Default value.
+ s64 value = 65530;
+
+ // Try to read how many mappings we can make.
+ std::ifstream s("/proc/sys/vm/max_map_count");
+ s >> value;
+
+ // Print, for debug.
+ LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
+
+ // Allow 20000 maps for other code and to account for split inaccuracy.
+ return std::max<s64>(value - 20000, 0);
+}
+
+} // namespace
+
+HeapTracker::HeapTracker(Common::HostMemory& buffer)
+ : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
+HeapTracker::~HeapTracker() = default;
+
+void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
+ MemoryPermission perm, bool is_separate_heap) {
+ // When mapping other memory, map pages immediately.
+ if (!is_separate_heap) {
+ m_buffer.Map(virtual_offset, host_offset, length, perm, false);
+ return;
+ }
+
+ {
+ // We are mapping part of a separate heap.
+ std::scoped_lock lk{m_lock};
+
+ auto* const map = new SeparateHeapMap{
+ .vaddr = virtual_offset,
+ .paddr = host_offset,
+ .size = length,
+ .tick = m_tick++,
+ .perm = perm,
+ .is_resident = false,
+ };
+
+ // Insert into mappings.
+ m_map_count++;
+ m_mappings.insert(*map);
+ }
+
+ // Finally, map.
+ this->DeferredMapSeparateHeap(virtual_offset);
+}
+
+void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
+ // If this is a separate heap...
+ if (is_separate_heap) {
+ std::scoped_lock lk{m_lock};
+
+ const SeparateHeapMap key{
+ .vaddr = virtual_offset,
+ };
+
+ // Split at the boundaries of the region we are removing.
+ this->SplitHeapMapLocked(virtual_offset);
+ this->SplitHeapMapLocked(virtual_offset + size);
+
+ // Erase all mappings in range.
+ auto it = m_mappings.find(key);
+ while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
+ // Get underlying item.
+ auto* const item = std::addressof(*it);
+
+ // If resident, erase from resident map.
+ if (item->is_resident) {
+ ASSERT(--m_resident_map_count >= 0);
+ m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
+ }
+
+ // Erase from map.
+ ASSERT(--m_map_count >= 0);
+ it = m_mappings.erase(it);
+
+ // Free the item.
+ delete item;
+ }
+ }
+
+ // Unmap pages.
+ m_buffer.Unmap(virtual_offset, size, false);
+}
+
+void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
+ // Ensure no rebuild occurs while reprotecting.
+ std::shared_lock lk{m_rebuild_lock};
+
+ // Split at the boundaries of the region we are reprotecting.
+ this->SplitHeapMap(virtual_offset, size);
+
+ // Declare tracking variables.
+ const VAddr end = virtual_offset + size;
+ VAddr cur = virtual_offset;
+
+ while (cur < end) {
+ VAddr next = cur;
+ bool should_protect = false;
+
+ {
+ std::scoped_lock lk2{m_lock};
+
+ const SeparateHeapMap key{
+ .vaddr = next,
+ };
+
+ // Try to get the next mapping corresponding to this address.
+ const auto it = m_mappings.nfind(key);
+
+ if (it == m_mappings.end()) {
+ // There are no separate heap mappings remaining.
+ next = end;
+ should_protect = true;
+ } else if (it->vaddr == cur) {
+ // We are in range.
+ // Update permission bits.
+ it->perm = perm;
+
+ // Determine next address and whether we should protect.
+ next = cur + it->size;
+ should_protect = it->is_resident;
+ } else /* if (it->vaddr > cur) */ {
+ // We weren't in range, but there is a block coming up that will be.
+ next = it->vaddr;
+ should_protect = true;
+ }
+ }
+
+ // Clamp to end.
+ next = std::min(next, end);
+
+ // Reprotect, if we need to.
+ if (should_protect) {
+ m_buffer.Protect(cur, next - cur, perm);
+ }
+
+ // Advance.
+ cur = next;
+ }
+}
+
+bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
+ if (m_buffer.IsInVirtualRange(fault_address)) {
+ return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
+ }
+
+ return false;
+}
+
+bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
+ bool rebuild_required = false;
+
+ {
+ std::scoped_lock lk{m_lock};
+
+ // Check to ensure this was a non-resident separate heap mapping.
+ const auto it = this->GetNearestHeapMapLocked(virtual_offset);
+ if (it == m_mappings.end() || it->is_resident) {
+ return false;
+ }
+
+ // Update tick before possible rebuild.
+ it->tick = m_tick++;
+
+ // Check if we need to rebuild.
+ if (m_resident_map_count > m_max_resident_map_count) {
+ rebuild_required = true;
+ }
+
+ // Map the area.
+ m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
+
+ // This map is now resident.
+ it->is_resident = true;
+ m_resident_map_count++;
+ m_resident_mappings.insert(*it);
+ }
+
+ if (rebuild_required) {
+ // A rebuild was required, so perform it now.
+ this->RebuildSeparateHeapAddressSpace();
+ }
+
+ return true;
+}
+
+void HeapTracker::RebuildSeparateHeapAddressSpace() {
+ std::scoped_lock lk{m_rebuild_lock, m_lock};
+
+ ASSERT(!m_resident_mappings.empty());
+
+ // Dump half of the mappings.
+ //
+ // Despite being worse in theory, this has proven to be better in practice than more
+ // regularly dumping a smaller amount, because it significantly reduces average case
+ // lock contention.
+ const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
+ const size_t evict_count = m_resident_map_count - desired_count;
+ auto it = m_resident_mappings.begin();
+
+ for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
+ // Unmark and unmap.
+ it->is_resident = false;
+ m_buffer.Unmap(it->vaddr, it->size, false);
+
+ // Advance.
+ ASSERT(--m_resident_map_count >= 0);
+ it = m_resident_mappings.erase(it);
+ }
+}
+
+void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
+ std::scoped_lock lk{m_lock};
+
+ this->SplitHeapMapLocked(offset);
+ this->SplitHeapMapLocked(offset + size);
+}
+
+void HeapTracker::SplitHeapMapLocked(VAddr offset) {
+ const auto it = this->GetNearestHeapMapLocked(offset);
+ if (it == m_mappings.end() || it->vaddr == offset) {
+ // Not contained or no split required.
+ return;
+ }
+
+ // Cache the original values.
+ auto* const left = std::addressof(*it);
+ const size_t orig_size = left->size;
+
+ // Adjust the left map.
+ const size_t left_size = offset - left->vaddr;
+ left->size = left_size;
+
+ // Create the new right map.
+ auto* const right = new SeparateHeapMap{
+ .vaddr = left->vaddr + left_size,
+ .paddr = left->paddr + left_size,
+ .size = orig_size - left_size,
+ .tick = left->tick,
+ .perm = left->perm,
+ .is_resident = left->is_resident,
+ };
+
+ // Insert the new right map.
+ m_map_count++;
+ m_mappings.insert(*right);
+
+ // If resident, also insert into resident map.
+ if (right->is_resident) {
+ m_resident_map_count++;
+ m_resident_mappings.insert(*right);
+ }
+}
+
+HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
+ const SeparateHeapMap key{
+ .vaddr = offset,
+ };
+
+ return m_mappings.find(key);
+}
+
+} // namespace Common
diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h
new file mode 100644
index 000000000..ee5b0bf43
--- /dev/null
+++ b/src/common/heap_tracker.h
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <mutex>
+#include <set>
+#include <shared_mutex>
+
+#include "common/host_memory.h"
+#include "common/intrusive_red_black_tree.h"
+
+namespace Common {
+
+struct SeparateHeapMap {
+ Common::IntrusiveRedBlackTreeNode addr_node{};
+ Common::IntrusiveRedBlackTreeNode tick_node{};
+ VAddr vaddr{};
+ PAddr paddr{};
+ size_t size{};
+ size_t tick{};
+ MemoryPermission perm{};
+ bool is_resident{};
+};
+
+struct SeparateHeapMapAddrComparator {
+ static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
+ if (lhs.vaddr < rhs.vaddr) {
+ return -1;
+ } else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+};
+
+struct SeparateHeapMapTickComparator {
+ static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
+ if (lhs.tick < rhs.tick) {
+ return -1;
+ } else if (lhs.tick > rhs.tick) {
+ return 1;
+ } else {
+ return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
+ }
+ }
+};
+
+class HeapTracker {
+public:
+ explicit HeapTracker(Common::HostMemory& buffer);
+ ~HeapTracker();
+
+ void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
+ bool is_separate_heap);
+ void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
+ void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
+ u8* VirtualBasePointer() {
+ return m_buffer.VirtualBasePointer();
+ }
+
+ bool DeferredMapSeparateHeap(u8* fault_address);
+ bool DeferredMapSeparateHeap(size_t virtual_offset);
+
+private:
+ using AddrTreeTraits =
+ Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
+ using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
+
+ using TickTreeTraits =
+ Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
+ using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
+
+ AddrTree m_mappings{};
+ TickTree m_resident_mappings{};
+
+private:
+ void SplitHeapMap(VAddr offset, size_t size);
+ void SplitHeapMapLocked(VAddr offset);
+
+ AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
+
+ void RebuildSeparateHeapAddressSpace();
+
+private:
+ Common::HostMemory& m_buffer;
+ const s64 m_max_resident_map_count;
+
+ std::shared_mutex m_rebuild_lock{};
+ std::mutex m_lock{};
+ s64 m_map_count{};
+ s64 m_resident_map_count{};
+ size_t m_tick{};
+};
+
+} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index e540375b8..860c39e6a 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
- MemoryPermission perms) {
+ MemoryPermission perms, bool separate_heap) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
@@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
}
-void HostMemory::Unmap(size_t virtual_offset, size_t length) {
+void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
@@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
impl->Unmap(virtual_offset + virtual_base_offset, length);
}
-void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
- bool execute) {
+void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) {
return;
}
+ const bool read = True(perm & MemoryPermission::Read);
+ const bool write = True(perm & MemoryPermission::Write);
+ const bool execute = True(perm & MemoryPermission::Execute);
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
}
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 747c5850c..72fbb05af 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -40,11 +40,12 @@ public:
HostMemory(HostMemory&& other) noexcept;
HostMemory& operator=(HostMemory&& other) noexcept;
- void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms);
+ void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
+ bool separate_heap);
- void Unmap(size_t virtual_offset, size_t length);
+ void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
- void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false);
+ void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
void EnableDirectMappedAddress();
@@ -64,6 +65,10 @@ public:
return virtual_base;
}
+ bool IsInVirtualRange(void* address) const noexcept {
+ return address >= virtual_base && address < virtual_base + virtual_size;
+ }
+
private:
size_t backing_size{};
size_t virtual_size{};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d4f27197c..7a267f8c0 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -208,6 +208,10 @@ public:
instance->StartBackendThread();
}
+ static void Stop() {
+ instance->StopBackendThread();
+ }
+
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
@@ -259,6 +263,15 @@ private:
});
}
+ void StopBackendThread() {
+ backend_thread.request_stop();
+ if (backend_thread.joinable()) {
+ backend_thread.join();
+ }
+
+ ForEachBackend([](Backend& backend) { backend.Flush(); });
+ }
+
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string&& message) const {
using std::chrono::duration_cast;
@@ -313,6 +326,10 @@ void Start() {
Impl::Start();
}
+void Stop() {
+ Impl::Stop();
+}
+
void DisableLoggingInTests() {
initialization_in_progress_suppress_logging = true;
}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 12e5e2498..2a9926e9e 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -14,6 +14,9 @@ void Initialize();
void Start();
+/// Explicitly stops the logger thread and flushes the buffers
+void Stop();
+
void DisableLoggingInTests();
/**
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 5c961b202..e7e9fdb38 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -103,7 +103,7 @@ private:
// Having them on the same cache-line would result in false-sharing between them.
// TODO: Remove this ifdef whenever clang and GCC support
// std::hardware_destructive_interference_size.
-#if defined(_MSC_VER) && _MSC_VER >= 1911
+#ifdef __cpp_lib_hardware_interference_size
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
#else
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index ea52bbfa6..07709d4e5 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
case Category::CpuDebug:
case Category::CpuUnsafe:
return "Cpu";
+ case Category::Overlay:
+ return "Overlay";
case Category::Renderer:
case Category::RendererAdvanced:
case Category::RendererDebug:
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index c82e17495..1a290ad77 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -18,6 +18,7 @@ enum class Category : u32 {
Cpu,
CpuDebug,
CpuUnsafe,
+ Overlay,
Renderer,
RendererAdvanced,
RendererDebug,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 96ab39cb8..e2ec2164c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -549,21 +549,34 @@ add_library(core STATIC
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
+ hle/service/hid/controllers/npad/npad_data.cpp
+ hle/service/hid/controllers/npad/npad_data.h
+ hle/service/hid/controllers/npad/npad_resource.cpp
+ hle/service/hid/controllers/npad/npad_resource.h
hle/service/hid/controllers/types/debug_pad_types.h
hle/service/hid/controllers/types/keyboard_types.h
hle/service/hid/controllers/types/mouse_types.h
hle/service/hid/controllers/types/npad_types.h
+ hle/service/hid/controllers/types/shared_memory_format.h
hle/service/hid/controllers/types/touch_types.h
hle/service/hid/controllers/applet_resource.cpp
hle/service/hid/controllers/applet_resource.h
+ hle/service/hid/controllers/capture_button.cpp
+ hle/service/hid/controllers/capture_button.h
hle/service/hid/controllers/console_six_axis.cpp
hle/service/hid/controllers/console_six_axis.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
+ hle/service/hid/controllers/debug_mouse.cpp
+ hle/service/hid/controllers/debug_mouse.h
hle/service/hid/controllers/debug_pad.cpp
hle/service/hid/controllers/debug_pad.h
+ hle/service/hid/controllers/digitizer.cpp
+ hle/service/hid/controllers/digitizer.h
hle/service/hid/controllers/gesture.cpp
hle/service/hid/controllers/gesture.h
+ hle/service/hid/controllers/home_button.cpp
+ hle/service/hid/controllers/home_button.h
hle/service/hid/controllers/keyboard.cpp
hle/service/hid/controllers/keyboard.h
hle/service/hid/controllers/mouse.cpp
@@ -574,15 +587,16 @@ add_library(core STATIC
hle/service/hid/controllers/palma.h
hle/service/hid/controllers/seven_six_axis.cpp
hle/service/hid/controllers/seven_six_axis.h
- hle/service/hid/controllers/shared_memory_format.h
hle/service/hid/controllers/shared_memory_holder.cpp
hle/service/hid/controllers/shared_memory_holder.h
hle/service/hid/controllers/six_axis.cpp
hle/service/hid/controllers/six_axis.h
- hle/service/hid/controllers/stubbed.cpp
- hle/service/hid/controllers/stubbed.h
+ hle/service/hid/controllers/sleep_button.cpp
+ hle/service/hid/controllers/sleep_button.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
+ hle/service/hid/controllers/unique_pad.cpp
+ hle/service/hid/controllers/unique_pad.h
hle/service/hid/hidbus/hidbus_base.cpp
hle/service/hid/hidbus/hidbus_base.h
hle/service/hid/hidbus/ringcon.cpp
@@ -978,6 +992,7 @@ endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE
+ arm/dynarmic/arm_dynarmic.cpp
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h
@@ -987,6 +1002,8 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
arm/dynarmic/dynarmic_cp15.h
arm/dynarmic/dynarmic_exclusive_monitor.cpp
arm/dynarmic/dynarmic_exclusive_monitor.h
+ hle/service/jit/jit_code_memory.cpp
+ hle/service/jit/jit_code_memory.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 698c9c8ad..5dc7e5d59 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -9,7 +9,7 @@
namespace Core {
-void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const {
+void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
Kernel::Svc::ThreadContext ctx;
this->GetContext(ctx);
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 806c7c9e9..495963eef 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -95,7 +95,7 @@ public:
virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
// Stack trace generation.
- void LogBacktrace(const Kernel::KProcess* process) const;
+ void LogBacktrace(Kernel::KProcess* process) const;
// Debug functionality.
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
diff --git a/src/core/arm/debug.cpp b/src/core/arm/debug.cpp
index af1c34bc3..854509463 100644
--- a/src/core/arm/debug.cpp
+++ b/src/core/arm/debug.cpp
@@ -79,7 +79,7 @@ constexpr std::array<u64, 2> SegmentBases{
0x7100000000ULL,
};
-void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
+void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
auto modules = FindModules(process);
const bool is_64 = process->Is64Bit();
@@ -118,7 +118,7 @@ void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<Backtrace
}
}
-std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
+std::vector<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) {
std::vector<BacktraceEntry> out;
auto& memory = process->GetMemory();
@@ -144,7 +144,7 @@ std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
return out;
}
-std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
+std::vector<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) {
std::vector<BacktraceEntry> out;
auto& memory = process->GetMemory();
@@ -173,7 +173,7 @@ std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
} // namespace
std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
- const auto* process = thread->GetOwnerProcess();
+ auto* process = thread->GetOwnerProcess();
if (process->Is64Bit()) {
return GetNameFromThreadType64(process->GetMemory(), *thread);
} else {
@@ -248,7 +248,7 @@ Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
return cur_addr - 1;
}
-Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
+Loader::AppLoader::Modules FindModules(Kernel::KProcess* process) {
Loader::AppLoader::Modules modules;
auto& page_table = process->GetPageTable();
@@ -312,7 +312,7 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
return modules;
}
-Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) {
+Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process) {
// Do we have any loaded executable sections?
auto modules = FindModules(process);
@@ -337,7 +337,7 @@ void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 addres
}
}
-std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process,
+std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) {
if (process->Is64Bit()) {
return GetAArch64Backtrace(process, ctx);
diff --git a/src/core/arm/debug.h b/src/core/arm/debug.h
index c542633db..3cd671365 100644
--- a/src/core/arm/debug.h
+++ b/src/core/arm/debug.h
@@ -14,9 +14,9 @@ std::optional<std::string> GetThreadName(const Kernel::KThread* thread);
std::string_view GetThreadWaitReason(const Kernel::KThread* thread);
std::string GetThreadState(const Kernel::KThread* thread);
-Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process);
+Loader::AppLoader::Modules FindModules(Kernel::KProcess* process);
Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base);
-Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process);
+Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process);
void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size);
@@ -28,7 +28,7 @@ struct BacktraceEntry {
std::string name;
};
-std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process,
+std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx);
std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread);
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
new file mode 100644
index 000000000..e6e9fc45b
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifdef __linux__
+
+#include "common/signal_chain.h"
+
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/memory.h"
+
+namespace Core {
+
+namespace {
+
+thread_local Core::Memory::Memory* g_current_memory{};
+std::once_flag g_registered{};
+struct sigaction g_old_segv {};
+
+void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
+ if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
+ return;
+ }
+
+ return g_old_segv.sa_sigaction(sig, info, ctx);
+}
+
+} // namespace
+
+ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
+ g_current_memory = std::addressof(process->GetMemory());
+}
+
+ScopedJitExecution::~ScopedJitExecution() {
+ g_current_memory = nullptr;
+}
+
+void ScopedJitExecution::RegisterHandler() {
+ std::call_once(g_registered, [] {
+ struct sigaction sa {};
+ sa.sa_sigaction = &HandleSigSegv;
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
+ });
+}
+
+} // namespace Core
+
+#endif
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index eef7c3116..53dd18815 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
return static_cast<HaltReason>(hr);
}
+#ifdef __linux__
+
+class ScopedJitExecution {
+public:
+ explicit ScopedJitExecution(Kernel::KProcess* process);
+ ~ScopedJitExecution();
+ static void RegisterHandler();
+};
+
+#else
+
+class ScopedJitExecution {
+public:
+ explicit ScopedJitExecution(Kernel::KProcess* process) {}
+ ~ScopedJitExecution() {}
+ static void RegisterHandler() {}
+};
+
+#endif
+
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index f34865e26..36478f722 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
- explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process)
+ explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process)
: m_parent{parent}, m_memory(process->GetMemory()),
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
m_check_memory_access{m_debugger_enabled ||
@@ -169,7 +169,7 @@ public:
ArmDynarmic32& m_parent;
Core::Memory::Memory& m_memory;
- const Kernel::KProcess* m_process{};
+ Kernel::KProcess* m_process{};
const bool m_debugger_enabled{};
const bool m_check_memory_access{};
static constexpr u64 MinimumRunCycles = 10000U;
@@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
}
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
+ ScopedJitExecution sj(thread->GetOwnerProcess());
+
m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run());
}
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
+ ScopedJitExecution sj(thread->GetOwnerProcess());
+
m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step());
}
@@ -370,13 +374,14 @@ void ArmDynarmic32::RewindBreakpointInstruction() {
this->SetContext(m_breakpoint_context);
}
-ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
+ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)),
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
m_jit = MakeJit(&page_table_impl);
+ ScopedJitExecution::RegisterHandler();
}
ArmDynarmic32::~ArmDynarmic32() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 185ac7cbf..b580efe61 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -20,7 +20,7 @@ class System;
class ArmDynarmic32 final : public ArmInterface {
public:
- ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
+ ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ArmDynarmic32() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index dff14756e..c811c8ad5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
- explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process)
+ explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process)
: m_parent{parent}, m_memory(process->GetMemory()),
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
m_check_memory_access{m_debugger_enabled ||
@@ -216,7 +216,7 @@ public:
Core::Memory::Memory& m_memory;
u64 m_tpidrro_el0{};
u64 m_tpidr_el0{};
- const Kernel::KProcess* m_process{};
+ Kernel::KProcess* m_process{};
const bool m_debugger_enabled{};
const bool m_check_memory_access{};
static constexpr u64 MinimumRunCycles = 10000U;
@@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
}
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
+ ScopedJitExecution sj(thread->GetOwnerProcess());
+
m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run());
}
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
+ ScopedJitExecution sj(thread->GetOwnerProcess());
+
m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step());
}
@@ -399,13 +403,14 @@ void ArmDynarmic64::RewindBreakpointInstruction() {
this->SetContext(m_breakpoint_context);
}
-ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
+ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} {
auto& page_table = process->GetPageTable().GetBasePageTable();
auto& page_table_impl = page_table.GetImpl();
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
+ ScopedJitExecution::RegisterHandler();
}
ArmDynarmic64::~ArmDynarmic64() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 4f3dd026f..08cd982b3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -25,7 +25,7 @@ class System;
class ArmDynarmic64 final : public ArmInterface {
public:
- ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process,
+ ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ArmDynarmic64() override;
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp
index 1311e66a9..123b3da7e 100644
--- a/src/core/arm/nce/arm_nce.cpp
+++ b/src/core/arm/nce/arm_nce.cpp
@@ -39,7 +39,7 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
}
using namespace Common::Literals;
-constexpr u32 StackSize = 32_KiB;
+constexpr u32 StackSize = 128_KiB;
} // namespace
diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp
index 8e81c66a5..def888d15 100644
--- a/src/core/arm/nce/interpreter_visitor.cpp
+++ b/src/core/arm/nce/interpreter_visitor.cpp
@@ -5,8 +5,6 @@
#include "common/bit_cast.h"
#include "core/arm/nce/interpreter_visitor.h"
-#include <dynarmic/frontend/A64/decoder/a64.h>
-
namespace Core {
template <u32 BitSize>
@@ -249,6 +247,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
return false;
}
+ // Size in bytes
const u64 size = 4 << opc.ZeroExtend();
const u64 offset = imm19.SignExtend<u64>() << 2;
const u64 address = this->GetPc() + offset;
@@ -530,7 +529,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
}
case MemOp::Load: {
u128 data{};
- m_memory.ReadBlock(address, &data, datasize);
+ m_memory.ReadBlock(address, &data, datasize / 8);
this->SetVec(Vt, data);
break;
}
diff --git a/src/core/arm/nce/visitor_base.h b/src/core/arm/nce/visitor_base.h
index 8fb032912..6a2be3d9b 100644
--- a/src/core/arm/nce/visitor_base.h
+++ b/src/core/arm/nce/visitor_base.h
@@ -4,9 +4,15 @@
#pragma once
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
#include <dynarmic/frontend/A64/a64_types.h>
+#include <dynarmic/frontend/A64/decoder/a64.h>
#include <dynarmic/frontend/imm.h>
+#pragma GCC diagnostic pop
+
namespace Core {
class VisitorBase {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b14f74976..66f444d39 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -28,7 +28,6 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
-#include "core/gpu_dirty_memory_manager.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
@@ -130,11 +129,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
- cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
- time_manager{system}, gpu_dirty_memory_write_manager{} {
- memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
- }
+ : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
+ reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
void Initialize(System& system) {
device_memory = std::make_unique<Core::DeviceMemory>();
@@ -241,17 +237,17 @@ struct System::Impl {
debugger = std::make_unique<Debugger>(system, port);
}
- SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
+ void InitializeKernel(System& system) {
LOG_DEBUG(Core, "initialized OK");
// Setting changes may require a full system reinitialization (e.g., disabling multicore).
ReinitializeIfNecessary(system);
- memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
-
kernel.Initialize();
cpu_manager.Initialize();
+ }
+ SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
/// Reset all glue registrations
arp_manager.ResetAll();
@@ -300,17 +296,9 @@ struct System::Impl {
return SystemResultStatus::ErrorGetLoader;
}
- SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
- if (init_result != SystemResultStatus::Success) {
- LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
- static_cast<int>(init_result));
- ShutdownMainProcess();
- return init_result;
- }
-
- telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
+ InitializeKernel(system);
- // Create the process.
+ // Create the application process.
auto main_process = Kernel::KProcess::Create(system.Kernel());
Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.AppendNewProcess(main_process);
@@ -323,7 +311,18 @@ struct System::Impl {
return static_cast<SystemResultStatus>(
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
}
+
+ // Set up the rest of the system.
+ SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
+ if (init_result != SystemResultStatus::Success) {
+ LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
+ static_cast<int>(init_result));
+ ShutdownMainProcess();
+ return init_result;
+ }
+
AddGlueRegistrationForProcess(*app_loader, *main_process);
+ telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
// Initialize cheat engine
if (cheat_engine) {
@@ -426,7 +425,6 @@ struct System::Impl {
cpu_manager.Shutdown();
debugger.reset();
kernel.Shutdown();
- memory.Reset();
Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) {
@@ -507,7 +505,6 @@ struct System::Impl {
std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
std::unique_ptr<Core::DeviceMemory> device_memory;
std::unique_ptr<AudioCore::AudioCore> audio_core;
- Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
Network::RoomNetwork room_network;
@@ -567,9 +564,6 @@ struct System::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
- std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
- gpu_dirty_memory_write_manager{};
-
std::deque<std::vector<u8>> user_channel;
};
@@ -652,29 +646,12 @@ void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index);
}
-Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
- const std::size_t core = impl->kernel.GetCurrentHostThreadID();
- return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
- ? core
- : Core::Hardware::NUM_CPU_CORES - 1];
-}
-
-/// Provides a constant reference to the current gou dirty memory manager.
-const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
- const std::size_t core = impl->kernel.GetCurrentHostThreadID();
- return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
- ? core
- : Core::Hardware::NUM_CPU_CORES - 1];
-}
-
size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID();
}
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
- for (auto& manager : impl->gpu_dirty_memory_write_manager) {
- manager.Gather(callback);
- }
+ return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
}
PerfStatsResults System::GetAndResetPerfStats() {
@@ -723,20 +700,12 @@ const Kernel::KProcess* System::ApplicationProcess() const {
return impl->kernel.ApplicationProcess();
}
-ExclusiveMonitor& System::Monitor() {
- return impl->kernel.GetExclusiveMonitor();
-}
-
-const ExclusiveMonitor& System::Monitor() const {
- return impl->kernel.GetExclusiveMonitor();
-}
-
Memory::Memory& System::ApplicationMemory() {
- return impl->memory;
+ return impl->kernel.ApplicationProcess()->GetMemory();
}
const Core::Memory::Memory& System::ApplicationMemory() const {
- return impl->memory;
+ return impl->kernel.ApplicationProcess()->GetMemory();
}
Tegra::GPU& System::GPU() {
diff --git a/src/core/core.h b/src/core/core.h
index 473204db7..ba5add0dc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -116,7 +116,6 @@ class CpuManager;
class Debugger;
class DeviceMemory;
class ExclusiveMonitor;
-class GPUDirtyMemoryManager;
class PerfStats;
class Reporter;
class SpeedLimiter;
@@ -225,12 +224,6 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
- /// Provides a reference to the gou dirty memory manager.
- [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
-
- /// Provides a constant reference to the current gou dirty memory manager.
- [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
-
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
[[nodiscard]] size_t GetCurrentHostThreadID() const;
@@ -250,12 +243,6 @@ public:
/// Gets a const reference to the underlying CPU manager
[[nodiscard]] const CpuManager& GetCpuManager() const;
- /// Gets a reference to the exclusive monitor
- [[nodiscard]] ExclusiveMonitor& Monitor();
-
- /// Gets a constant reference to the exclusive monitor
- [[nodiscard]] const ExclusiveMonitor& Monitor() const;
-
/// Gets a mutable reference to the system memory instance.
[[nodiscard]] Core::Memory::Memory& ApplicationMemory();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index d6b5abc68..fc536413b 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -29,7 +29,6 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
struct CoreTiming::Event {
s64 time;
u64 fifo_order;
- std::uintptr_t user_data;
std::weak_ptr<EventType> type;
s64 reschedule_time;
heap_t::handle_type handle{};
@@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0;
shutting_down = false;
cpu_ticks = 0;
- const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
- -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
- ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
}
}
void CoreTiming::ClearPendingEvents() {
- std::scoped_lock lock{basic_lock};
+ std::scoped_lock lock{advance_lock, basic_lock};
event_queue.clear();
+ event.Set();
}
void CoreTiming::Pause(bool is_paused) {
@@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const {
}
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
- const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data, bool absolute_time) {
+ const std::shared_ptr<EventType>& event_type, bool absolute_time) {
{
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
- auto h{event_queue.emplace(
- Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
+ auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
(*h).handle = h;
}
@@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data, bool absolute_time) {
+ bool absolute_time) {
{
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
- auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type,
- resched_time.count()})};
+ auto h{event_queue.emplace(
+ Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
(*h).handle = h;
}
@@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
}
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data, bool wait) {
+ UnscheduleEventType type) {
{
std::scoped_lock lk{basic_lock};
std::vector<heap_t::handle_type> to_remove;
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
const Event& e = *itr;
- if (e.type.lock().get() == event_type.get() && e.user_data == user_data) {
+ if (e.type.lock().get() == event_type.get()) {
to_remove.push_back(itr->handle);
}
}
@@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
for (auto h : to_remove) {
event_queue.erase(h);
}
+
+ event_type->sequence_number++;
}
// Force any in-progress events to finish
- if (wait) {
+ if (type == UnscheduleEventType::Wait) {
std::scoped_lock lk{advance_lock};
}
}
@@ -208,28 +205,31 @@ std::optional<s64> CoreTiming::Advance() {
const Event& evt = event_queue.top();
if (const auto event_type{evt.type.lock()}) {
- if (evt.reschedule_time == 0) {
- const auto evt_user_data = evt.user_data;
- const auto evt_time = evt.time;
+ const auto evt_time = evt.time;
+ const auto evt_sequence_num = event_type->sequence_number;
+ if (evt.reschedule_time == 0) {
event_queue.pop();
basic_lock.unlock();
event_type->callback(
- evt_user_data, evt_time,
- std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
+ evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
basic_lock.lock();
} else {
basic_lock.unlock();
const auto new_schedule_time{event_type->callback(
- evt.user_data, evt.time,
- std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
+ evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
basic_lock.lock();
+ if (evt_sequence_num != event_type->sequence_number) {
+ // Heap handle is invalidated after external modification.
+ continue;
+ }
+
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
@@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
next_time = pause_end_time + next_schedule_time;
}
- event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data,
- evt.type, next_schedule_time, evt.handle});
+ event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
+ next_schedule_time, evt.handle});
}
}
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 21548f0a9..7e4dff7f3 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -22,17 +22,25 @@ namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
- std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
+ s64 time, std::chrono::nanoseconds ns_late)>;
/// Contains the characteristics of a particular event.
struct EventType {
explicit EventType(TimedCallback&& callback_, std::string&& name_)
- : callback{std::move(callback_)}, name{std::move(name_)} {}
+ : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
/// The event's callback function.
TimedCallback callback;
/// A pointer to the name of the event.
const std::string name;
+ /// A monotonic sequence number, incremented when this event is
+ /// changed externally.
+ size_t sequence_number;
+};
+
+enum class UnscheduleEventType {
+ Wait,
+ NoWait,
};
/**
@@ -89,23 +97,17 @@ public:
/// Schedules an event in core timing
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
- const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
- bool absolute_time = false);
+ const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
/// Schedules an event which will automatically re-schedule itself with the given time, until
/// unscheduled
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data = 0, bool absolute_time = false);
+ bool absolute_time = false);
- void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
- bool wait = true);
-
- void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data) {
- UnscheduleEvent(event_type, user_data, false);
- }
+ void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
+ UnscheduleEventType type = UnscheduleEventType::Wait);
void AddTicks(u64 ticks_to_add);
@@ -158,7 +160,6 @@ private:
heap_t event_queue;
u64 event_fifo_id = 0;
- std::shared_ptr<EventType> ev_lost;
Common::Event event{};
Common::Event pause_event{};
mutable std::mutex basic_lock;
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 7be1322cc..31033634c 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -73,6 +73,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
return nullptr;
auto in_data = in->ReadAllBytes();
+ if (in_data.size() == 0) {
+ return nullptr;
+ }
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header
@@ -88,6 +91,10 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
else
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
+ if (real_offset > in_data.size()) {
+ return nullptr;
+ }
+
u16 data_size{};
if (ips->ReadObject(&data_size, offset) != sizeof(u16))
return nullptr;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 763a44fee..539c7f7af 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
return npdm_header.system_resource_size;
}
+PoolPartition ProgramMetadata::GetPoolPartition() const {
+ return acid_header.pool_partition;
+}
+
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
return aci_kernel_capabilities;
}
@@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
// Begin ACID printing (potential perms, signed)
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
- LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
+ LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 76ee97d78..a53092b87 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
Everything = 1ULL << 63,
};
+enum class PoolPartition : u32 {
+ Application = 0,
+ Applet = 1,
+ System = 2,
+ SystemNonSecure = 3,
+};
+
/**
* Helper which implements an interface to parse Program Description Metadata (NPDM)
* Data can either be loaded from a file path or with data and an offset into it.
@@ -72,6 +79,7 @@ public:
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
u32 GetSystemResourceSize() const;
+ PoolPartition GetPoolPartition() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
const std::array<u8, 0x10>& GetName() const {
return npdm_header.application_name;
@@ -116,8 +124,9 @@ private:
union {
u32 flags;
- BitField<0, 1, u32> is_retail;
- BitField<1, 31, u32> flags_unk;
+ BitField<0, 1, u32> production_flag;
+ BitField<1, 1, u32> unqualified_approval;
+ BitField<2, 4, PoolPartition> pool_partition;
};
u64_le title_id_min;
u64_le title_id_max;
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 4bf285f36..a81ed6af0 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -267,6 +267,7 @@ enum class NpadStyleSet : u32 {
All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
+DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet)
// This is nn::hid::VibrationDevicePosition
enum class VibrationDevicePosition : u32 {
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 78d43d729..48889253d 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -4,6 +4,7 @@
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.h"
+#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
@@ -26,9 +27,9 @@ bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
return true;
}
-bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) {
- auto& monitor = system.Monitor();
- const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
+bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
+ auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
+ const auto current_core = kernel.CurrentPhysicalCoreIndex();
// NOTE: If scheduler lock is not held here, interrupt disable is required.
// KScopedInterruptDisable di;
@@ -66,10 +67,10 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
return true;
}
-bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value,
+bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
s32 new_value) {
- auto& monitor = system.Monitor();
- const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
+ auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
+ const auto current_core = kernel.CurrentPhysicalCoreIndex();
// NOTE: If scheduler lock is not held here, interrupt disable is required.
// KScopedInterruptDisable di;
@@ -159,7 +160,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
// Check the userspace value.
s32 user_value{};
- R_UNLESS(UpdateIfEqual(m_system, std::addressof(user_value), addr, value, value + 1),
+ R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
ResultInvalidCurrentMemory);
R_UNLESS(user_value == value, ResultInvalidState);
@@ -219,7 +220,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
s32 user_value{};
bool succeeded{};
if (value != new_value) {
- succeeded = UpdateIfEqual(m_system, std::addressof(user_value), addr, value, new_value);
+ succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
} else {
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
}
@@ -262,7 +263,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
s32 user_value{};
bool succeeded{};
if (decrement) {
- succeeded = DecrementIfLessThan(m_system, std::addressof(user_value), addr, value);
+ succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
} else {
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
}
diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp
index 636b3f993..7bea1a1c2 100644
--- a/src/core/hle/kernel/k_auto_object_container.cpp
+++ b/src/core/hle/kernel/k_auto_object_container.cpp
@@ -8,19 +8,22 @@
namespace Kernel {
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
- KScopedLightLock lk(m_lock);
+ // KScopedInterruptDisable di;
+ KScopedSpinLock lk(m_lock);
m_object_list.insert_unique(*obj);
}
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
- KScopedLightLock lk(m_lock);
+ // KScopedInterruptDisable di;
+ KScopedSpinLock lk(m_lock);
m_object_list.erase(*obj);
}
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
- KScopedLightLock lk(m_lock);
+ // KScopedInterruptDisable di;
+ KScopedSpinLock lk(m_lock);
return std::count_if(m_object_list.begin(), m_object_list.end(),
[&](const auto& obj) { return obj.GetOwner() == owner; });
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
index badd75d2a..770743d21 100644
--- a/src/core/hle/kernel/k_auto_object_container.h
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -7,7 +7,7 @@
#include "common/common_funcs.h"
#include "core/hle/kernel/k_auto_object.h"
-#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_spin_lock.h"
namespace Kernel {
@@ -21,32 +21,7 @@ public:
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
- class ListAccessor : public KScopedLightLock {
- public:
- explicit ListAccessor(KAutoObjectWithListContainer* container)
- : KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
- explicit ListAccessor(KAutoObjectWithListContainer& container)
- : KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
-
- typename ListType::iterator begin() const {
- return m_list.begin();
- }
-
- typename ListType::iterator end() const {
- return m_list.end();
- }
-
- typename ListType::iterator find(typename ListType::const_reference ref) const {
- return m_list.find(ref);
- }
-
- private:
- ListType& m_list;
- };
-
- friend class ListAccessor;
-
- KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
+ KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
void Initialize() {}
void Finalize() {}
@@ -56,7 +31,7 @@ public:
size_t GetOwnedCount(KProcess* owner);
private:
- KLightLock m_lock;
+ KSpinLock m_lock;
ListType m_object_list;
};
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index 274fee493..d2288c30d 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -185,6 +185,10 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
case RegionType::NoMapping:
break;
case RegionType::KernelTraceBuffer:
+ if constexpr (!IsKTraceEnabled) {
+ break;
+ }
+ [[fallthrough]];
case RegionType::OnMemoryBootImage:
case RegionType::DTB:
R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
@@ -330,8 +334,6 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTab
// Map the range.
R_TRY(this->MapRange_(cap, size_cap, page_table));
- } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
- continue;
} else {
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
}
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 11b1b977e..68cea978a 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
KSession* session{};
// Reserve a new session from the resource limit.
- //! FIXME: we are reserving this from the wrong resource limit!
- KScopedResourceReservation session_reservation(
- m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
+ KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
+ LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Allocate a session normally.
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7633a51fb..94ea3527a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -28,10 +28,10 @@ bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
return true;
}
-bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero,
+bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 if_zero,
u32 new_orr_mask) {
- auto& monitor = system.Monitor();
- const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
+ auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
+ const auto current_core = kernel.CurrentPhysicalCoreIndex();
u32 expected{};
@@ -208,7 +208,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) [[likely]] {
- UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag,
+ UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index d7660630c..1bf68e6b0 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -90,8 +90,7 @@ public:
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
- //! FIXME: this is the wrong process!
- auto* const cur_process = m_kernel.ApplicationProcess();
+ auto* const cur_process = GetCurrentProcessPointer(m_kernel);
ASSERT(cur_process != nullptr);
return cur_process;
}
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 8e2e40307..4e947dd6b 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -10,15 +10,15 @@ namespace Kernel {
void KHardwareTimer::Initialize() {
// Create the timing callback to register with CoreTiming.
- m_event_type = Core::Timing::CreateEvent(
- "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
- reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
- return std::nullopt;
- });
+ m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
+ [this](s64, std::chrono::nanoseconds) {
+ this->DoTask();
+ return std::nullopt;
+ });
}
void KHardwareTimer::Finalize() {
- m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+ m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
m_wakeup_time = std::numeric_limits<s64>::max();
m_event_type.reset();
}
@@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
m_wakeup_time = wakeup_time;
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
- m_event_type, reinterpret_cast<uintptr_t>(this),
- true);
+ m_event_type, true);
}
void KHardwareTimer::DisableInterrupt() {
- m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
- reinterpret_cast<uintptr_t>(this));
+ m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
+ Core::Timing::UnscheduleEventType::NoWait);
m_wakeup_time = std::numeric_limits<s64>::max();
}
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 423289145..8c1549559 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
void KPageTableBase::Finalize() {
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
if (Settings::IsFastmemEnabled()) {
- m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
+ m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
}
};
@@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
// Unmap.
R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
cur_pages, 0, false, unmap_properties,
- OperationType::Unmap, true));
+ OperationType::UnmapPhysical, true));
}
// Check if we're done.
@@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
// Map the papges.
R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
cur_pg, map_properties,
- OperationType::MapFirstGroup, false));
+ OperationType::MapFirstGroupPhysical, false));
}
}
@@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
// Unmap.
R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
- unmap_properties, OperationType::Unmap, false));
+ unmap_properties, OperationType::UnmapPhysical, false));
}
// Check if we're done.
@@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
// or free them to the page list, and so it goes unused (along with page properties).
switch (operation) {
- case OperationType::Unmap: {
+ case OperationType::Unmap:
+ case OperationType::UnmapPhysical: {
+ const bool separate_heap = operation == OperationType::UnmapPhysical;
+
// Ensure that any pages we track are closed on exit.
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
@@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
// Unmap.
- m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize);
+ m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
R_SUCCEED();
}
@@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
ASSERT(virt_addr != 0);
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
- ConvertToMemoryPermission(properties.perm));
+ ConvertToMemoryPermission(properties.perm), false);
// Open references to pages, if we should.
if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
switch (operation) {
case OperationType::MapGroup:
- case OperationType::MapFirstGroup: {
+ case OperationType::MapFirstGroup:
+ case OperationType::MapFirstGroupPhysical: {
+ const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
+
// We want to maintain a new reference to every page in the group.
- KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
+ KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
for (const auto& node : page_group) {
const size_t size{node.GetNumPages() * PageSize};
// Map the pages.
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
- ConvertToMemoryPermission(properties.perm));
+ ConvertToMemoryPermission(properties.perm), separate_heap);
virt_addr += size;
}
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 556d230b3..077cafc96 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -104,6 +104,9 @@ protected:
ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7,
+
+ MapFirstGroupPhysical = 65000,
+ UnmapPhysical = 65001,
};
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 3a2635e1f..068e71dff 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
params.code_address, params.code_num_pages * PageSize,
- m_system_resource, res_limit, this->GetMemory(), 0));
+ m_system_resource, res_limit, m_memory, 0));
}
ON_RESULT_FAILURE_2 {
m_page_table.Finalize();
};
+ // Ensure our memory is initialized.
+ m_memory.SetCurrentPageTable(*this);
+ m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
+
// Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
KMemoryState::Code),
@@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
params.code_address, code_size, m_system_resource, res_limit,
- this->GetMemory(), aslr_space_start));
+ m_memory, aslr_space_start));
}
ON_RESULT_FAILURE_2 {
m_page_table.Finalize();
};
+ // Ensure our memory is initialized.
+ m_memory.SetCurrentPageTable(*this);
+ m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
+
// Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
ResultInvalidMemoryRegion);
@@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
s32 max_out_count) {
- // TODO: use current memory reference
- auto& memory = m_kernel.System().ApplicationMemory();
+ auto& memory = this->GetMemory();
// Lock the list.
KScopedLightLock lk(m_list_lock);
@@ -1128,14 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
- m_handle_table{kernel} {}
+ m_handle_table{kernel}, m_dirty_memory_managers{},
+ m_exclusive_monitor{}, m_memory{kernel.System()} {}
KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, bool is_hbl) {
// Create a resource limit for the process.
- const auto physical_memory_size =
- m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
+ const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
+ const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
auto* res_limit =
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
@@ -1146,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
Svc::CreateProcessFlag flag{};
u64 code_address{};
- // We are an application.
- flag |= Svc::CreateProcessFlag::IsApplication;
+ // Determine if we are an application.
+ if (pool == KMemoryManager::Pool::Application) {
+ flag |= Svc::CreateProcessFlag::IsApplication;
+ }
// If we are 64-bit, create as such.
if (metadata.Is64BitProgram()) {
@@ -1196,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
std::memcpy(params.name.data(), name.data(), sizeof(params.name));
// Initialize for application process.
- R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit,
- KMemoryManager::Pool::Application, aslr_space_start));
+ R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
+ aslr_space_start));
// Assign remaining properties.
m_is_hbl = is_hbl;
@@ -1223,22 +1233,25 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef HAS_NCE
- if (Settings::IsNceEnabled()) {
+ if (this->IsApplication() && Settings::IsNceEnabled()) {
auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment();
const auto& patch = code_set.PatchSegment();
- buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
- buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
+ buffer.Protect(GetInteger(base_addr + code.addr), code.size,
+ Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
+ buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
+ Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
}
#endif
}
void KProcess::InitializeInterfaces() {
- this->GetMemory().SetCurrentPageTable(*this);
+ m_exclusive_monitor =
+ Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
#ifdef HAS_NCE
- if (this->Is64Bit() && Settings::IsNceEnabled()) {
+ if (this->IsApplication() && Settings::IsNceEnabled()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
}
@@ -1248,13 +1261,13 @@ void KProcess::InitializeInterfaces() {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
m_kernel.System(), m_kernel.IsMulticore(), this,
- static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
+ static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
}
} else {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
m_kernel.System(), m_kernel.IsMulticore(), this,
- static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i);
+ static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
}
}
}
@@ -1305,9 +1318,10 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
return true;
}
-Core::Memory::Memory& KProcess::GetMemory() const {
- // TODO: per-process memory
- return m_kernel.System().ApplicationMemory();
+void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
+ for (auto& manager : m_dirty_memory_managers) {
+ manager.Gather(callback);
+ }
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 4b114e39b..53c0e3316 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -7,6 +7,7 @@
#include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h"
+#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h"
@@ -17,6 +18,7 @@
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
+#include "core/memory.h"
namespace Kernel {
@@ -126,6 +128,9 @@ private:
#ifdef HAS_NCE
std::unordered_map<u64, u64> m_post_handlers{};
#endif
+ std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
+ std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
+ Core::Memory::Memory m_memory;
private:
Result StartTermination();
@@ -502,7 +507,15 @@ public:
void InitializeInterfaces();
- Core::Memory::Memory& GetMemory() const;
+ Core::Memory::Memory& GetMemory() {
+ return m_memory;
+ }
+
+ void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
+
+ Core::ExclusiveMonitor& GetExclusiveMonitor() const {
+ return *m_exclusive_monitor;
+ }
public:
// Overridden parent functions.
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index e33a88e24..adaabdd6d 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -8,6 +8,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
+#include "common/scratch_buffer.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_client_port.h"
@@ -29,12 +30,138 @@ namespace Kernel {
namespace {
+constexpr inline size_t PointerTransferBufferAlignment = 0x10;
+constexpr inline size_t ReceiveListDataSize =
+ MessageBuffer::MessageHeader::ReceiveListCountType_CountMax *
+ MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32);
+
+using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
+
+class ReceiveList {
+public:
+ static constexpr int GetEntryCount(const MessageBuffer::MessageHeader& header) {
+ const auto count = header.GetReceiveListCount();
+ switch (count) {
+ case MessageBuffer::MessageHeader::ReceiveListCountType_None:
+ return 0;
+ case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer:
+ return 0;
+ case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer:
+ return 1;
+ default:
+ return count - MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset;
+ }
+ }
+
+ explicit ReceiveList(const u32* dst_msg, uint64_t dst_address,
+ KProcessPageTable& dst_page_table,
+ const MessageBuffer::MessageHeader& dst_header,
+ const MessageBuffer::SpecialHeader& dst_special_header, size_t msg_size,
+ size_t out_offset, s32 dst_recv_list_idx, bool is_tls) {
+ m_recv_list_count = dst_header.GetReceiveListCount();
+ m_msg_buffer_end = dst_address + sizeof(u32) * out_offset;
+ m_msg_buffer_space_end = dst_address + msg_size;
+
+ // NOTE: Nintendo calculates the receive list index here using the special header.
+ // We pre-calculate it in the caller, and pass it as a parameter.
+ (void)dst_special_header;
+
+ const u32* recv_list = dst_msg + dst_recv_list_idx;
+ const auto entry_count = GetEntryCount(dst_header);
+
+ if (is_tls) {
+ // Messages from TLS to TLS are contained within one page.
+ std::memcpy(m_data.data(), recv_list,
+ entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
+ } else {
+ // If any buffer is not from TLS, perform a normal read instead.
+ uint64_t cur_addr = dst_address + dst_recv_list_idx * sizeof(u32);
+ dst_page_table.GetMemory().ReadBlock(
+ cur_addr, m_data.data(),
+ entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
+ }
+ }
+
+ bool IsIndex() const {
+ return m_recv_list_count >
+ static_cast<s32>(MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset);
+ }
+
+ bool IsToMessageBuffer() const {
+ return m_recv_list_count ==
+ MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer;
+ }
+
+ void GetBuffer(uint64_t& out, size_t size, int& key) const {
+ switch (m_recv_list_count) {
+ case MessageBuffer::MessageHeader::ReceiveListCountType_None: {
+ out = 0;
+ break;
+ }
+ case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer: {
+ const uint64_t buf =
+ Common::AlignUp(m_msg_buffer_end + key, PointerTransferBufferAlignment);
+
+ if ((buf < buf + size) && (buf + size <= m_msg_buffer_space_end)) {
+ out = buf;
+ key = static_cast<int>(buf + size - m_msg_buffer_end);
+ } else {
+ out = 0;
+ }
+ break;
+ }
+ case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer: {
+ const MessageBuffer::ReceiveListEntry entry(m_data[0], m_data[1]);
+ const uint64_t buf =
+ Common::AlignUp(entry.GetAddress() + key, PointerTransferBufferAlignment);
+
+ const uint64_t entry_addr = entry.GetAddress();
+ const size_t entry_size = entry.GetSize();
+
+ if ((buf < buf + size) && (entry_addr < entry_addr + entry_size) &&
+ (buf + size <= entry_addr + entry_size)) {
+ out = buf;
+ key = static_cast<int>(buf + size - entry_addr);
+ } else {
+ out = 0;
+ }
+ break;
+ }
+ default: {
+ if (key < m_recv_list_count -
+ static_cast<s32>(
+ MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset)) {
+ const MessageBuffer::ReceiveListEntry entry(m_data[2 * key + 0],
+ m_data[2 * key + 1]);
+
+ const uintptr_t entry_addr = entry.GetAddress();
+ const size_t entry_size = entry.GetSize();
+
+ if ((entry_addr < entry_addr + entry_size) && (entry_size >= size)) {
+ out = entry_addr;
+ }
+ } else {
+ out = 0;
+ }
+ break;
+ }
+ }
+ }
+
+private:
+ std::array<u32, ReceiveListDataSize> m_data;
+ s32 m_recv_list_count;
+ uint64_t m_msg_buffer_end;
+ uint64_t m_msg_buffer_space_end;
+};
+
template <bool MoveHandleAllowed>
-Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread,
- MessageBuffer& dst_msg, const MessageBuffer& src_msg,
- MessageBuffer::SpecialHeader& src_special_header) {
+Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& src_process,
+ KThread& src_thread, const MessageBuffer& dst_msg,
+ const MessageBuffer& src_msg,
+ const MessageBuffer::SpecialHeader& src_special_header) {
// Copy the special header to the destination.
- s32 offset = dst_msg.Set(src_special_header);
+ offset = dst_msg.Set(src_special_header);
// Copy the process ID.
if (src_special_header.GetHasProcessId()) {
@@ -110,6 +237,102 @@ Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, K
R_RETURN(result);
}
+Result ProcessReceiveMessagePointerDescriptors(int& offset, int& pointer_key,
+ KProcessPageTable& dst_page_table,
+ KProcessPageTable& src_page_table,
+ const MessageBuffer& dst_msg,
+ const MessageBuffer& src_msg,
+ const ReceiveList& dst_recv_list, bool dst_user) {
+ // Get the offset at the start of processing.
+ const int cur_offset = offset;
+
+ // Get the pointer desc.
+ MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
+ offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
+
+ // Extract address/size.
+ const uint64_t src_pointer = src_desc.GetAddress();
+ const size_t recv_size = src_desc.GetSize();
+ uint64_t recv_pointer = 0;
+
+ // Process the buffer, if it has a size.
+ if (recv_size > 0) {
+ // If using indexing, set index.
+ if (dst_recv_list.IsIndex()) {
+ pointer_key = src_desc.GetIndex();
+ }
+
+ // Get the buffer.
+ dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
+ R_UNLESS(recv_pointer != 0, ResultOutOfResource);
+
+ // Perform the pointer data copy.
+ if (dst_user) {
+ R_TRY(src_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
+ dst_page_table, recv_pointer, recv_size, KMemoryState::FlagReferenceCounted,
+ KMemoryState::FlagReferenceCounted,
+ KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
+ KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
+ src_pointer, KMemoryState::FlagLinearMapped, KMemoryState::FlagLinearMapped,
+ KMemoryPermission::UserRead, KMemoryAttribute::Uncached, KMemoryAttribute::None));
+ } else {
+ R_TRY(src_page_table.CopyMemoryFromLinearToUser(
+ recv_pointer, recv_size, src_pointer, KMemoryState::FlagLinearMapped,
+ KMemoryState::FlagLinearMapped, KMemoryPermission::UserRead,
+ KMemoryAttribute::Uncached, KMemoryAttribute::None));
+ }
+ }
+
+ // Set the output descriptor.
+ dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
+ recv_size, src_desc.GetIndex()));
+
+ R_SUCCEED();
+}
+
+constexpr Result GetMapAliasMemoryState(KMemoryState& out,
+ MessageBuffer::MapAliasDescriptor::Attribute attr) {
+ switch (attr) {
+ case MessageBuffer::MapAliasDescriptor::Attribute::Ipc:
+ out = KMemoryState::Ipc;
+ break;
+ case MessageBuffer::MapAliasDescriptor::Attribute::NonSecureIpc:
+ out = KMemoryState::NonSecureIpc;
+ break;
+ case MessageBuffer::MapAliasDescriptor::Attribute::NonDeviceIpc:
+ out = KMemoryState::NonDeviceIpc;
+ break;
+ default:
+ R_THROW(ResultInvalidCombination);
+ }
+
+ R_SUCCEED();
+}
+
+constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state,
+ KMemoryAttribute& out_attr_mask,
+ KMemoryState state) {
+ switch (state) {
+ case KMemoryState::Ipc:
+ out_state = KMemoryState::FlagCanUseIpc;
+ out_attr_mask =
+ KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
+ break;
+ case KMemoryState::NonSecureIpc:
+ out_state = KMemoryState::FlagCanUseNonSecureIpc;
+ out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
+ break;
+ case KMemoryState::NonDeviceIpc:
+ out_state = KMemoryState::FlagCanUseNonDeviceIpc;
+ out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
+ break;
+ default:
+ R_THROW(ResultInvalidCombination);
+ }
+
+ R_SUCCEED();
+}
+
void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
// Parse the message.
const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
@@ -144,166 +367,855 @@ void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buff
}
}
-} // namespace
+Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size,
+ KPhysicalAddress message_paddr) {
+ // Server is assumed to be current thread.
+ KThread& thread = GetCurrentThread(kernel);
-using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
+ // Get the linear message pointer.
+ u32* msg_ptr;
+ if (message) {
+ msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(message_paddr);
+ } else {
+ msg_ptr = GetCurrentMemory(kernel).GetPointer<u32>(thread.GetTlsAddress());
+ buffer_size = MessageBufferSize;
+ message = GetInteger(thread.GetTlsAddress());
+ }
-KServerSession::KServerSession(KernelCore& kernel)
- : KSynchronizationObject{kernel}, m_lock{m_kernel} {}
+ // Parse the message.
+ const MessageBuffer msg(msg_ptr, buffer_size);
+ const MessageBuffer::MessageHeader header(msg);
+ const MessageBuffer::SpecialHeader special_header(msg, header);
-KServerSession::~KServerSession() = default;
+ // Check that the size is big enough.
+ R_UNLESS(MessageBuffer::GetMessageBufferSize(header, special_header) <= buffer_size,
+ ResultInvalidCombination);
+
+ // If there's a special header, there may be move handles we need to close.
+ if (header.GetHasSpecialHeader()) {
+ // Determine the offset to the start of handles.
+ auto offset = msg.GetSpecialDataIndex(header, special_header);
+ if (special_header.GetHasProcessId()) {
+ offset += static_cast<int>(sizeof(u64) / sizeof(u32));
+ }
+ if (auto copy_count = special_header.GetCopyHandleCount(); copy_count > 0) {
+ offset += static_cast<int>((sizeof(Svc::Handle) * copy_count) / sizeof(u32));
+ }
-void KServerSession::Destroy() {
- m_parent->OnServerClosed();
+ // Get the handle table.
+ auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
- this->CleanupRequests();
+ // Close the handles.
+ for (auto i = 0; i < special_header.GetMoveHandleCount(); ++i) {
+ handle_table.Remove(msg.GetHandle(offset));
+ offset += static_cast<int>(sizeof(Svc::Handle) / sizeof(u32));
+ }
+ }
- m_parent->Close();
+ R_SUCCEED();
}
-void KServerSession::OnClientClosed() {
- KScopedLightLock lk{m_lock};
+Result CleanupServerMap(KSessionRequest* request, KProcess* server_process) {
+ // If there's no server process, there's nothing to clean up.
+ R_SUCCEED_IF(server_process == nullptr);
- // Handle any pending requests.
- KSessionRequest* prev_request = nullptr;
- while (true) {
- // Declare variables for processing the request.
- KSessionRequest* request = nullptr;
- KEvent* event = nullptr;
- KThread* thread = nullptr;
- bool cur_request = false;
- bool terminate = false;
+ // Get the page table.
+ auto& server_page_table = server_process->GetPageTable();
- // Get the next request.
- {
- KScopedSchedulerLock sl{m_kernel};
+ // Cleanup Send mappings.
+ for (size_t i = 0; i < request->GetSendCount(); ++i) {
+ R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i),
+ request->GetSendSize(i),
+ request->GetSendMemoryState(i)));
+ }
- if (m_current_request != nullptr && m_current_request != prev_request) {
- // Set the request, open a reference as we process it.
- request = m_current_request;
- request->Open();
- cur_request = true;
+ // Cleanup Receive mappings.
+ for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
+ R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i),
+ request->GetReceiveSize(i),
+ request->GetReceiveMemoryState(i)));
+ }
- // Get thread and event for the request.
- thread = request->GetThread();
- event = request->GetEvent();
+ // Cleanup Exchange mappings.
+ for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
+ R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i),
+ request->GetExchangeSize(i),
+ request->GetExchangeMemoryState(i)));
+ }
- // If the thread is terminating, handle that.
- if (thread->IsTerminationRequested()) {
- request->ClearThread();
- request->ClearEvent();
- terminate = true;
- }
+ R_SUCCEED();
+}
- prev_request = request;
- } else if (!m_request_list.empty()) {
- // Pop the request from the front of the list.
- request = std::addressof(m_request_list.front());
- m_request_list.pop_front();
+Result CleanupClientMap(KSessionRequest* request, KProcessPageTable* client_page_table) {
+ // If there's no client page table, there's nothing to clean up.
+ R_SUCCEED_IF(client_page_table == nullptr);
- // Get thread and event for the request.
- thread = request->GetThread();
- event = request->GetEvent();
- }
+ // Cleanup Send mappings.
+ for (size_t i = 0; i < request->GetSendCount(); ++i) {
+ R_TRY(client_page_table->CleanupForIpcClient(request->GetSendClientAddress(i),
+ request->GetSendSize(i),
+ request->GetSendMemoryState(i)));
+ }
+
+ // Cleanup Receive mappings.
+ for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
+ R_TRY(client_page_table->CleanupForIpcClient(request->GetReceiveClientAddress(i),
+ request->GetReceiveSize(i),
+ request->GetReceiveMemoryState(i)));
+ }
+
+ // Cleanup Exchange mappings.
+ for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
+ R_TRY(client_page_table->CleanupForIpcClient(request->GetExchangeClientAddress(i),
+ request->GetExchangeSize(i),
+ request->GetExchangeMemoryState(i)));
+ }
+
+ R_SUCCEED();
+}
+
+Result CleanupMap(KSessionRequest* request, KProcess* server_process,
+ KProcessPageTable* client_page_table) {
+ // Cleanup the server map.
+ R_TRY(CleanupServerMap(request, server_process));
+
+ // Cleanup the client map.
+ R_TRY(CleanupClientMap(request, client_page_table));
+
+ R_SUCCEED();
+}
+
+Result ProcessReceiveMessageMapAliasDescriptors(int& offset, KProcessPageTable& dst_page_table,
+ KProcessPageTable& src_page_table,
+ const MessageBuffer& dst_msg,
+ const MessageBuffer& src_msg,
+ KSessionRequest* request, KMemoryPermission perm,
+ bool send) {
+ // Get the offset at the start of processing.
+ const int cur_offset = offset;
+
+ // Get the map alias descriptor.
+ MessageBuffer::MapAliasDescriptor src_desc(src_msg, cur_offset);
+ offset += static_cast<int>(MessageBuffer::MapAliasDescriptor::GetDataSize() / sizeof(u32));
+
+ // Extract address/size.
+ const KProcessAddress src_address = src_desc.GetAddress();
+ const size_t size = src_desc.GetSize();
+ KProcessAddress dst_address = 0;
+
+ // Determine the result memory state.
+ KMemoryState dst_state;
+ R_TRY(GetMapAliasMemoryState(dst_state, src_desc.GetAttribute()));
+
+ // Process the buffer, if it has a size.
+ if (size > 0) {
+ // Set up the source pages for ipc.
+ R_TRY(dst_page_table.SetupForIpc(std::addressof(dst_address), size, src_address,
+ src_page_table, perm, dst_state, send));
+
+ // Ensure that we clean up on failure.
+ ON_RESULT_FAILURE {
+ dst_page_table.CleanupForIpcServer(dst_address, size, dst_state);
+ src_page_table.CleanupForIpcClient(src_address, size, dst_state);
+ };
+
+ // Push the appropriate mapping.
+ if (perm == KMemoryPermission::UserRead) {
+ R_TRY(request->PushSend(src_address, dst_address, size, dst_state));
+ } else if (send) {
+ R_TRY(request->PushExchange(src_address, dst_address, size, dst_state));
+ } else {
+ R_TRY(request->PushReceive(src_address, dst_address, size, dst_state));
}
+ }
- // If there are no requests, we're done.
- if (request == nullptr) {
- break;
+ // Set the output descriptor.
+ dst_msg.Set(cur_offset,
+ MessageBuffer::MapAliasDescriptor(reinterpret_cast<void*>(GetInteger(dst_address)),
+ size, src_desc.GetAttribute()));
+
+ R_SUCCEED();
+}
+
+Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_message_buffer,
+ size_t dst_buffer_size, KPhysicalAddress dst_message_paddr,
+ KThread& src_thread, uint64_t src_message_buffer, size_t src_buffer_size,
+ KServerSession* session, KSessionRequest* request) {
+ // Prepare variables for receive.
+ KThread& dst_thread = GetCurrentThread(kernel);
+ KProcess& dst_process = *(dst_thread.GetOwnerProcess());
+ KProcess& src_process = *(src_thread.GetOwnerProcess());
+ auto& dst_page_table = dst_process.GetPageTable();
+ auto& src_page_table = src_process.GetPageTable();
+
+ // NOTE: Session is used only for debugging, and so may go unused.
+ (void)session;
+
+ // The receive list is initially not broken.
+ recv_list_broken = false;
+
+ // Set the server process for the request.
+ request->SetServerProcess(std::addressof(dst_process));
+
+ // Determine the message buffers.
+ u32 *dst_msg_ptr, *src_msg_ptr;
+ bool dst_user, src_user;
+
+ if (dst_message_buffer) {
+ dst_msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(dst_message_paddr);
+ dst_user = true;
+ } else {
+ dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
+ dst_buffer_size = MessageBufferSize;
+ dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
+ dst_user = false;
+ }
+
+ if (src_message_buffer) {
+ // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
+ src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
+ src_user = true;
+ } else {
+ src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
+ src_buffer_size = MessageBufferSize;
+ src_message_buffer = GetInteger(src_thread.GetTlsAddress());
+ src_user = false;
+ }
+
+ // Parse the headers.
+ const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
+ const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
+ const MessageBuffer::MessageHeader dst_header(dst_msg);
+ const MessageBuffer::MessageHeader src_header(src_msg);
+ const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
+ const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
+
+ // Get the end of the source message.
+ const size_t src_end_offset =
+ MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
+
+ // Ensure that the headers fit.
+ R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <= dst_buffer_size,
+ ResultInvalidCombination);
+ R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <= src_buffer_size,
+ ResultInvalidCombination);
+
+ // Ensure the receive list offset is after the end of raw data.
+ if (dst_header.GetReceiveListOffset()) {
+ R_UNLESS(dst_header.GetReceiveListOffset() >=
+ MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
+ dst_header.GetRawCount(),
+ ResultInvalidCombination);
+ }
+
+ // Ensure that the destination buffer is big enough to receive the source.
+ R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
+
+ // Get the receive list.
+ const s32 dst_recv_list_idx =
+ MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
+ ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
+ dst_special_header, dst_buffer_size, src_end_offset,
+ dst_recv_list_idx, !dst_user);
+
+ // Ensure that the source special header isn't invalid.
+ const bool src_has_special_header = src_header.GetHasSpecialHeader();
+ if (src_has_special_header) {
+ // Sending move handles from client -> server is not allowed.
+ R_UNLESS(src_special_header.GetMoveHandleCount() == 0, ResultInvalidCombination);
+ }
+
+ // Prepare for further processing.
+ int pointer_key = 0;
+ int offset = dst_msg.Set(src_header);
+
+ // Set up a guard to make sure that we end up in a clean state on error.
+ ON_RESULT_FAILURE {
+ // Cleanup mappings.
+ CleanupMap(request, std::addressof(dst_process), std::addressof(src_page_table));
+
+ // Cleanup special data.
+ if (src_header.GetHasSpecialHeader()) {
+ CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
}
- // All requests must have threads.
- ASSERT(thread != nullptr);
+ // Cleanup the header if the receive list isn't broken.
+ if (!recv_list_broken) {
+ dst_msg.Set(dst_header);
+ if (dst_header.GetHasSpecialHeader()) {
+ dst_msg.Set(dst_special_header);
+ }
+ }
+ };
+
+ // Process any special data.
+ if (src_header.GetHasSpecialHeader()) {
+ // After we process, make sure we track whether the receive list is broken.
+ SCOPE_EXIT({
+ if (offset > dst_recv_list_idx) {
+ recv_list_broken = true;
+ }
+ });
- // Ensure that we close the request when done.
- SCOPE_EXIT({ request->Close(); });
+ // Process special data.
+ R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
+ dst_msg, src_msg, src_special_header));
+ }
- // If we're terminating, close a reference to the thread and event.
- if (terminate) {
- thread->Close();
- if (event != nullptr) {
- event->Close();
+ // Process any pointer buffers.
+ for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
+ // After we process, make sure we track whether the receive list is broken.
+ SCOPE_EXIT({
+ if (offset > dst_recv_list_idx) {
+ recv_list_broken = true;
+ }
+ });
+
+ R_TRY(ProcessReceiveMessagePointerDescriptors(
+ offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
+ dst_user && dst_header.GetReceiveListCount() ==
+ MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
+ }
+
+ // Process any map alias buffers.
+ for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
+ // After we process, make sure we track whether the receive list is broken.
+ SCOPE_EXIT({
+ if (offset > dst_recv_list_idx) {
+ recv_list_broken = true;
}
+ });
+
+ // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
+ const KMemoryPermission perm = (i >= src_header.GetSendCount())
+ ? KMemoryPermission::UserReadWrite
+ : KMemoryPermission::UserRead;
+
+ // Buffer is send if it is send or exch.
+ const bool send = (i < src_header.GetSendCount()) ||
+ (i >= src_header.GetSendCount() + src_header.GetReceiveCount());
+
+ R_TRY(ProcessReceiveMessageMapAliasDescriptors(offset, dst_page_table, src_page_table,
+ dst_msg, src_msg, request, perm, send));
+ }
+
+ // Process any raw data.
+ if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
+ // After we process, make sure we track whether the receive list is broken.
+ SCOPE_EXIT({
+ if (offset + raw_count > dst_recv_list_idx) {
+ recv_list_broken = true;
+ }
+ });
+
+ // Get the offset and size.
+ const size_t offset_words = offset * sizeof(u32);
+ const size_t raw_size = raw_count * sizeof(u32);
+
+ if (!dst_user && !src_user) {
+ // Fast case is TLS -> TLS, do raw memcpy if we can.
+ std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
+ } else if (dst_user) {
+ // Determine how much fast size we can copy.
+ const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
+ const size_t fast_size = max_fast_size - offset_words;
+
+ // Determine source state; if user buffer, we require heap, and otherwise only linear
+ // mapped (to enable tls use).
+ const auto src_state =
+ src_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
+
+ // Determine the source permission. User buffer should be unmapped + read, TLS should be
+ // user readable.
+ const KMemoryPermission src_perm = static_cast<KMemoryPermission>(
+ src_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelRead
+ : KMemoryPermission::UserRead);
+
+ // Perform the fast part of the copy.
+ R_TRY(src_page_table.CopyMemoryFromLinearToKernel(
+ dst_msg_ptr + offset, fast_size, src_message_buffer + offset_words, src_state,
+ src_state, src_perm, KMemoryAttribute::Uncached, KMemoryAttribute::None));
+
+ // If the fast part of the copy didn't get everything, perform the slow part of the
+ // copy.
+ if (fast_size < raw_size) {
+ R_TRY(src_page_table.CopyMemoryFromHeapToHeap(
+ dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
+ KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
+ KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
+ KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
+ src_message_buffer + max_fast_size, src_state, src_state, src_perm,
+ KMemoryAttribute::Uncached, KMemoryAttribute::None));
+ }
+ } else /* if (src_user) */ {
+ // The source is a user buffer, so it should be unmapped + readable.
+ constexpr KMemoryPermission SourcePermission = static_cast<KMemoryPermission>(
+ KMemoryPermission::NotMapped | KMemoryPermission::KernelRead);
+
+ // Copy the memory.
+ R_TRY(src_page_table.CopyMemoryFromLinearToUser(
+ dst_message_buffer + offset_words, raw_size, src_message_buffer + offset_words,
+ KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
+ SourcePermission, KMemoryAttribute::Uncached, KMemoryAttribute::None));
}
+ }
- // If we need to, reply.
- if (event != nullptr && !cur_request) {
- // There must be no mappings.
- ASSERT(request->GetSendCount() == 0);
- ASSERT(request->GetReceiveCount() == 0);
- ASSERT(request->GetExchangeCount() == 0);
+ // We succeeded!
+ R_SUCCEED();
+}
- // // Get the process and page table.
- // KProcess *client_process = thread->GetOwnerProcess();
- // auto& client_pt = client_process->GetPageTable();
+Result ProcessSendMessageReceiveMapping(KProcessPageTable& src_page_table,
+ KProcessPageTable& dst_page_table,
+ KProcessAddress client_address,
+ KProcessAddress server_address, size_t size,
+ KMemoryState src_state) {
+ // If the size is zero, there's nothing to process.
+ R_SUCCEED_IF(size == 0);
+
+ // Get the memory state and attribute mask to test.
+ KMemoryState test_state;
+ KMemoryAttribute test_attr_mask;
+ R_TRY(GetMapAliasTestStateAndAttributeMask(test_state, test_attr_mask, src_state));
+
+ // Determine buffer extents.
+ KProcessAddress aligned_dst_start = Common::AlignDown(GetInteger(client_address), PageSize);
+ KProcessAddress aligned_dst_end = Common::AlignUp(GetInteger(client_address) + size, PageSize);
+ KProcessAddress mapping_dst_start = Common::AlignUp(GetInteger(client_address), PageSize);
+ KProcessAddress mapping_dst_end =
+ Common::AlignDown(GetInteger(client_address) + size, PageSize);
+
+ KProcessAddress mapping_src_end =
+ Common::AlignDown(GetInteger(server_address) + size, PageSize);
+
+ // If the start of the buffer is unaligned, handle that.
+ if (aligned_dst_start != mapping_dst_start) {
+ ASSERT(client_address < mapping_dst_start);
+ const size_t copy_size = std::min<size_t>(size, mapping_dst_start - client_address);
+ R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
+ client_address, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
+ test_attr_mask, KMemoryAttribute::None, server_address));
+ }
- // // Reply to the request.
- // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
- // ResultSessionClosed);
+ // If the end of the buffer is unaligned, handle that.
+ if (mapping_dst_end < aligned_dst_end &&
+ (aligned_dst_start == mapping_dst_start || aligned_dst_start < mapping_dst_end)) {
+ const size_t copy_size = client_address + size - mapping_dst_end;
+ R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
+ mapping_dst_end, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
+ test_attr_mask, KMemoryAttribute::None, mapping_src_end));
+ }
- // // Unlock the buffer.
- // // NOTE: Nintendo does not check the result of this.
- // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
+ R_SUCCEED();
+}
- // Signal the event.
- event->Signal();
+Result ProcessSendMessagePointerDescriptors(int& offset, int& pointer_key,
+ KProcessPageTable& src_page_table,
+ KProcessPageTable& dst_page_table,
+ const MessageBuffer& dst_msg,
+ const MessageBuffer& src_msg,
+ const ReceiveList& dst_recv_list, bool dst_user) {
+ // Get the offset at the start of processing.
+ const int cur_offset = offset;
+
+ // Get the pointer desc.
+ MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
+ offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
+
+ // Extract address/size.
+ const uint64_t src_pointer = src_desc.GetAddress();
+ const size_t recv_size = src_desc.GetSize();
+ uint64_t recv_pointer = 0;
+
+ // Process the buffer, if it has a size.
+ if (recv_size > 0) {
+ // If using indexing, set index.
+ if (dst_recv_list.IsIndex()) {
+ pointer_key = src_desc.GetIndex();
}
+
+ // Get the buffer.
+ dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
+ R_UNLESS(recv_pointer != 0, ResultOutOfResource);
+
+ // Perform the pointer data copy.
+ const bool dst_heap = dst_user && dst_recv_list.IsToMessageBuffer();
+ const auto dst_state =
+ dst_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
+ const KMemoryPermission dst_perm =
+ dst_heap ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
+ : KMemoryPermission::UserReadWrite;
+ R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
+ recv_pointer, recv_size, dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
+ KMemoryAttribute::None, src_pointer));
}
- // Notify.
- this->NotifyAvailable(ResultSessionClosed);
+ // Set the output descriptor.
+ dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
+ recv_size, src_desc.GetIndex()));
+
+ R_SUCCEED();
}
-bool KServerSession::IsSignaled() const {
- ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
+Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_buffer_size,
+ KPhysicalAddress src_message_paddr, KThread& dst_thread,
+ uint64_t dst_message_buffer, size_t dst_buffer_size, KServerSession* session,
+ KSessionRequest* request) {
+ // Prepare variables for send.
+ KThread& src_thread = GetCurrentThread(kernel);
+ KProcess& dst_process = *(dst_thread.GetOwnerProcess());
+ KProcess& src_process = *(src_thread.GetOwnerProcess());
+ auto& dst_page_table = dst_process.GetPageTable();
+ auto& src_page_table = src_process.GetPageTable();
+
+ // NOTE: Session is used only for debugging, and so may go unused.
+ (void)session;
+
+ // Determine the message buffers.
+ u32 *dst_msg_ptr, *src_msg_ptr;
+ bool dst_user, src_user;
+
+ if (dst_message_buffer) {
+ // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
+ dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_message_buffer);
+ dst_user = true;
+ } else {
+ dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
+ dst_buffer_size = MessageBufferSize;
+ dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
+ dst_user = false;
+ }
- // If the client is closed, we're always signaled.
- if (m_parent->IsClientClosed()) {
- return true;
+ if (src_message_buffer) {
+ src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
+ src_user = true;
+ } else {
+ src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
+ src_buffer_size = MessageBufferSize;
+ src_message_buffer = GetInteger(src_thread.GetTlsAddress());
+ src_user = false;
}
- // Otherwise, we're signaled if we have a request and aren't handling one.
- return !m_request_list.empty() && m_current_request == nullptr;
+ // Parse the headers.
+ const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
+ const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
+ const MessageBuffer::MessageHeader dst_header(dst_msg);
+ const MessageBuffer::MessageHeader src_header(src_msg);
+ const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
+ const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
+
+ // Get the end of the source message.
+ const size_t src_end_offset =
+ MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
+
+ // Declare variables for processing.
+ int offset = 0;
+ int pointer_key = 0;
+ bool processed_special_data = false;
+
+ // Send the message.
+ {
+ // Make sure that we end up in a clean state on error.
+ ON_RESULT_FAILURE {
+ // Cleanup special data.
+ if (processed_special_data) {
+ if (src_header.GetHasSpecialHeader()) {
+ CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
+ }
+ } else {
+ CleanupServerHandles(kernel, src_user ? src_message_buffer : 0, src_buffer_size,
+ src_message_paddr);
+ }
+
+ // Cleanup mappings.
+ CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table));
+ };
+
+ // Ensure that the headers fit.
+ R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <=
+ src_buffer_size,
+ ResultInvalidCombination);
+ R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <=
+ dst_buffer_size,
+ ResultInvalidCombination);
+
+ // Ensure the receive list offset is after the end of raw data.
+ if (dst_header.GetReceiveListOffset()) {
+ R_UNLESS(dst_header.GetReceiveListOffset() >=
+ MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
+ dst_header.GetRawCount(),
+ ResultInvalidCombination);
+ }
+
+ // Ensure that the destination buffer is big enough to receive the source.
+ R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
+
+ // Replies must have no buffers.
+ R_UNLESS(src_header.GetSendCount() == 0, ResultInvalidCombination);
+ R_UNLESS(src_header.GetReceiveCount() == 0, ResultInvalidCombination);
+ R_UNLESS(src_header.GetExchangeCount() == 0, ResultInvalidCombination);
+
+ // Get the receive list.
+ const s32 dst_recv_list_idx =
+ MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
+ ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
+ dst_special_header, dst_buffer_size, src_end_offset,
+ dst_recv_list_idx, !dst_user);
+
+ // Handle any receive buffers.
+ for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
+ R_TRY(ProcessSendMessageReceiveMapping(
+ src_page_table, dst_page_table, request->GetReceiveClientAddress(i),
+ request->GetReceiveServerAddress(i), request->GetReceiveSize(i),
+ request->GetReceiveMemoryState(i)));
+ }
+
+ // Handle any exchange buffers.
+ for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
+ R_TRY(ProcessSendMessageReceiveMapping(
+ src_page_table, dst_page_table, request->GetExchangeClientAddress(i),
+ request->GetExchangeServerAddress(i), request->GetExchangeSize(i),
+ request->GetExchangeMemoryState(i)));
+ }
+
+ // Set the header.
+ offset = dst_msg.Set(src_header);
+
+ // Process any special data.
+ ASSERT(GetCurrentThreadPointer(kernel) == std::addressof(src_thread));
+ processed_special_data = true;
+ if (src_header.GetHasSpecialHeader()) {
+ R_TRY(ProcessMessageSpecialData<true>(offset, dst_process, src_process, src_thread,
+ dst_msg, src_msg, src_special_header));
+ }
+
+ // Process any pointer buffers.
+ for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
+ R_TRY(ProcessSendMessagePointerDescriptors(
+ offset, pointer_key, src_page_table, dst_page_table, dst_msg, src_msg,
+ dst_recv_list,
+ dst_user &&
+ dst_header.GetReceiveListCount() ==
+ MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
+ }
+
+ // Clear any map alias buffers.
+ for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
+ offset = dst_msg.Set(offset, MessageBuffer::MapAliasDescriptor());
+ }
+
+ // Process any raw data.
+ if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
+ // Get the offset and size.
+ const size_t offset_words = offset * sizeof(u32);
+ const size_t raw_size = raw_count * sizeof(u32);
+
+ if (!dst_user && !src_user) {
+ // Fast case is TLS -> TLS, do raw memcpy if we can.
+ std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
+ } else if (src_user) {
+ // Determine how much fast size we can copy.
+ const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
+ const size_t fast_size = max_fast_size - offset_words;
+
+ // Determine dst state; if user buffer, we require heap, and otherwise only linear
+ // mapped (to enable tls use).
+ const auto dst_state =
+ dst_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
+
+ // Determine the dst permission. User buffer should be unmapped + read, TLS should
+ // be user readable.
+ const KMemoryPermission dst_perm =
+ dst_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
+ : KMemoryPermission::UserReadWrite;
+
+ // Perform the fast part of the copy.
+ R_TRY(dst_page_table.CopyMemoryFromKernelToLinear(
+ dst_message_buffer + offset_words, fast_size, dst_state, dst_state, dst_perm,
+ KMemoryAttribute::Uncached, KMemoryAttribute::None, src_msg_ptr + offset));
+
+ // If the fast part of the copy didn't get everything, perform the slow part of the
+ // copy.
+ if (fast_size < raw_size) {
+ R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(
+ dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
+ dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
+ KMemoryAttribute::None, src_message_buffer + max_fast_size,
+ KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
+ KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
+ KMemoryAttribute::Uncached | KMemoryAttribute::Locked,
+ KMemoryAttribute::Locked));
+ }
+ } else /* if (dst_user) */ {
+ // The destination is a user buffer, so it should be unmapped + readable.
+ constexpr KMemoryPermission DestinationPermission =
+ KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
+
+ // Copy the memory.
+ R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
+ dst_message_buffer + offset_words, raw_size, KMemoryState::FlagReferenceCounted,
+ KMemoryState::FlagReferenceCounted, DestinationPermission,
+ KMemoryAttribute::Uncached, KMemoryAttribute::None,
+ src_message_buffer + offset_words));
+ }
+ }
+ }
+
+ // Perform (and validate) any remaining cleanup.
+ R_RETURN(CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table)));
}
-Result KServerSession::OnRequest(KSessionRequest* request) {
- // Create the wait queue.
- ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel};
+void ReplyAsyncError(KProcess* to_process, uint64_t to_msg_buf, size_t to_msg_buf_size,
+ Result result) {
+ // Convert the address to a linear pointer.
+ u32* to_msg = to_process->GetMemory().GetPointer<u32>(to_msg_buf);
+
+ // Set the error.
+ MessageBuffer msg(to_msg, to_msg_buf_size);
+ msg.SetAsyncResult(result);
+}
+
+} // namespace
+
+KServerSession::KServerSession(KernelCore& kernel)
+ : KSynchronizationObject{kernel}, m_lock{m_kernel} {}
+
+KServerSession::~KServerSession() = default;
+
+void KServerSession::Destroy() {
+ m_parent->OnServerClosed();
+
+ this->CleanupRequests();
+
+ m_parent->Close();
+}
+
+Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
+ KPhysicalAddress server_message_paddr,
+ std::shared_ptr<Service::HLERequestContext>* out_context,
+ std::weak_ptr<Service::SessionRequestManager> manager) {
+ // Lock the session.
+ KScopedLightLock lk{m_lock};
+
+ // Get the request and client thread.
+ KSessionRequest* request;
+ KThread* client_thread;
{
- // Lock the scheduler.
KScopedSchedulerLock sl{m_kernel};
- // Ensure that we can handle new requests.
- R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+ // Ensure that we can service the request.
+ R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
- // Check that we're not terminating.
- R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested);
+ // Ensure we aren't already servicing a request.
+ R_UNLESS(m_current_request == nullptr, ResultNotFound);
- // Get whether we're empty.
- const bool was_empty = m_request_list.empty();
+ // Ensure we have a request to service.
+ R_UNLESS(!m_request_list.empty(), ResultNotFound);
- // Add the request to the list.
- request->Open();
- m_request_list.push_back(*request);
+ // Pop the first request from the list.
+ request = std::addressof(m_request_list.front());
+ m_request_list.pop_front();
- // If we were empty, signal.
- if (was_empty) {
- this->NotifyAvailable();
+ // Get the thread for the request.
+ client_thread = request->GetThread();
+ R_UNLESS(client_thread != nullptr, ResultSessionClosed);
+
+ // Open the client thread.
+ client_thread->Open();
+ }
+
+ SCOPE_EXIT({ client_thread->Close(); });
+
+ // Set the request as our current.
+ m_current_request = request;
+
+ // Get the client address.
+ uint64_t client_message = request->GetAddress();
+ size_t client_buffer_size = request->GetSize();
+ bool recv_list_broken = false;
+
+ // Receive the message.
+ Result result = ResultSuccess;
+
+ if (out_context != nullptr) {
+ // HLE request.
+ if (!client_message) {
+ client_message = GetInteger(client_thread->GetTlsAddress());
}
+ Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
+ u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
+ *out_context =
+ std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
+ (*out_context)->SetSessionRequestManager(manager);
+ (*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
+ // We succeeded.
+ R_SUCCEED();
+ } else {
+ result = ReceiveMessage(m_kernel, recv_list_broken, server_message, server_buffer_size,
+ server_message_paddr, *client_thread, client_message,
+ client_buffer_size, this, request);
+ }
- // If we have a request event, this is asynchronous, and we don't need to wait.
- R_SUCCEED_IF(request->GetEvent() != nullptr);
+ // Handle cleanup on receive failure.
+ if (R_FAILED(result)) {
+ // Cache the result to return it to the client.
+ const Result result_for_client = result;
- // This is a synchronous request, so we should wait for our request to complete.
- GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
- GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
+ // Clear the current request.
+ {
+ KScopedSchedulerLock sl(m_kernel);
+ ASSERT(m_current_request == request);
+ m_current_request = nullptr;
+ if (!m_request_list.empty()) {
+ this->NotifyAvailable();
+ }
+ }
+
+ // Reply to the client.
+ {
+ // After we reply, close our reference to the request.
+ SCOPE_EXIT({ request->Close(); });
+
+ // Get the event to check whether the request is async.
+ if (KEvent* event = request->GetEvent(); event != nullptr) {
+ // The client sent an async request.
+ KProcess* client = client_thread->GetOwnerProcess();
+ auto& client_pt = client->GetPageTable();
+
+ // Send the async result.
+ if (R_FAILED(result_for_client)) {
+ ReplyAsyncError(client, client_message, client_buffer_size, result_for_client);
+ }
+
+ // Unlock the client buffer.
+ // NOTE: Nintendo does not check the result of this.
+ client_pt.UnlockForIpcUserBuffer(client_message, client_buffer_size);
+
+ // Signal the event.
+ event->Signal();
+ } else {
+ // End the client thread's wait.
+ KScopedSchedulerLock sl(m_kernel);
+
+ if (!client_thread->IsTerminationRequested()) {
+ client_thread->EndWait(result_for_client);
+ }
+ }
+ }
+
+ // Set the server result.
+ if (recv_list_broken) {
+ result = ResultReceiveListBroken;
+ } else {
+ result = ResultNotFound;
+ }
}
- return GetCurrentThread(m_kernel).GetWaitResult();
+ R_RETURN(result);
}
-Result KServerSession::SendReply(bool is_hle) {
+Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
+ KPhysicalAddress server_message_paddr, bool is_hle) {
// Lock the session.
KScopedLightLock lk{m_lock};
@@ -327,7 +1239,7 @@ Result KServerSession::SendReply(bool is_hle) {
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
- const uintptr_t client_message = request->GetAddress();
+ const uint64_t client_message = request->GetAddress();
const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
@@ -342,31 +1254,28 @@ Result KServerSession::SendReply(bool is_hle) {
// HLE servers write directly to a pointer to the thread command buffer. Therefore
// the reply has already been written in this case.
} else {
- Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
- KThread* server_thread = GetCurrentThreadPointer(m_kernel);
- KProcess& src_process = *client_thread->GetOwnerProcess();
- KProcess& dst_process = *server_thread->GetOwnerProcess();
- UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
-
- auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
- auto* dst_msg_buffer = memory.GetPointer<u32>(client_message);
- std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
-
- // Translate special header ad-hoc.
- MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
- MessageBuffer::MessageHeader src_header(src_msg);
- MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
- if (src_header.GetHasSpecialHeader()) {
- MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
- result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread,
- dst_msg, src_msg, src_special_header);
- if (R_FAILED(result)) {
- CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
- }
- }
+ result = SendMessage(m_kernel, server_message, server_buffer_size, server_message_paddr,
+ *client_thread, client_message, client_buffer_size, this, request);
+ }
+ } else if (!is_hle) {
+ // Otherwise, we'll need to do some cleanup.
+ KProcess* server_process = request->GetServerProcess();
+ KProcess* client_process =
+ (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
+ KProcessPageTable* client_page_table =
+ (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
+
+ // Cleanup server handles.
+ result = CleanupServerHandles(m_kernel, server_message, server_buffer_size,
+ server_message_paddr);
+
+ // Cleanup mappings.
+ Result cleanup_map_result = CleanupMap(request, server_process, client_page_table);
+
+ // If we successfully cleaned up handles, use the map cleanup result as our result.
+ if (R_SUCCEEDED(result)) {
+ result = cleanup_map_result;
}
- } else {
- result = ResultSessionClosed;
}
// Select a result for the client.
@@ -381,19 +1290,18 @@ Result KServerSession::SendReply(bool is_hle) {
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
- // // Get the client process/page table.
- // KProcess *client_process = client_thread->GetOwnerProcess();
- // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable());
+ // Get the client process/page table.
+ KProcess* client_process = client_thread->GetOwnerProcess();
+ KProcessPageTable* client_page_table = std::addressof(client_process->GetPageTable());
- // // If we need to, reply with an async error.
- // if (R_FAILED(client_result)) {
- // ReplyAsyncError(client_process, client_message, client_buffer_size,
- // client_result);
- // }
+ // If we need to, reply with an async error.
+ if (R_FAILED(client_result)) {
+ ReplyAsyncError(client_process, client_message, client_buffer_size, client_result);
+ }
- // // Unlock the client buffer.
- // // NOTE: Nintendo does not check the result of this.
- // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
+ // Unlock the client buffer.
+ // NOTE: Nintendo does not check the result of this.
+ client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
@@ -410,91 +1318,53 @@ Result KServerSession::SendReply(bool is_hle) {
R_RETURN(result);
}
-Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context,
- std::weak_ptr<Service::SessionRequestManager> manager) {
- // Lock the session.
- KScopedLightLock lk{m_lock};
-
- // Get the request and client thread.
- KSessionRequest* request;
- KThread* client_thread;
+Result KServerSession::OnRequest(KSessionRequest* request) {
+ // Create the wait queue.
+ ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel};
{
+ // Lock the scheduler.
KScopedSchedulerLock sl{m_kernel};
- // Ensure that we can service the request.
- R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
-
- // Ensure we aren't already servicing a request.
- R_UNLESS(m_current_request == nullptr, ResultNotFound);
+ // Ensure that we can handle new requests.
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
- // Ensure we have a request to service.
- R_UNLESS(!m_request_list.empty(), ResultNotFound);
+ // Check that we're not terminating.
+ R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested);
- // Pop the first request from the list.
- request = std::addressof(m_request_list.front());
- m_request_list.pop_front();
+ // Get whether we're empty.
+ const bool was_empty = m_request_list.empty();
- // Get the thread for the request.
- client_thread = request->GetThread();
- R_UNLESS(client_thread != nullptr, ResultSessionClosed);
+ // Add the request to the list.
+ request->Open();
+ m_request_list.push_back(*request);
- // Open the client thread.
- client_thread->Open();
- }
+ // If we were empty, signal.
+ if (was_empty) {
+ this->NotifyAvailable();
+ }
- SCOPE_EXIT({ client_thread->Close(); });
+ // If we have a request event, this is asynchronous, and we don't need to wait.
+ R_SUCCEED_IF(request->GetEvent() != nullptr);
- // Set the request as our current.
- m_current_request = request;
+ // This is a synchronous request, so we should wait for our request to complete.
+ GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
+ }
- // Get the client address.
- uintptr_t client_message = request->GetAddress();
- size_t client_buffer_size = request->GetSize();
- // bool recv_list_broken = false;
+ return GetCurrentThread(m_kernel).GetWaitResult();
+}
- if (!client_message) {
- client_message = GetInteger(client_thread->GetTlsAddress());
- client_buffer_size = MessageBufferSize;
- }
+bool KServerSession::IsSignaled() const {
+ ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
- // Receive the message.
- Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
- if (out_context != nullptr) {
- // HLE request.
- u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
- *out_context =
- std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
- (*out_context)->SetSessionRequestManager(manager);
- (*out_context)
- ->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
- } else {
- KThread* server_thread = GetCurrentThreadPointer(m_kernel);
- KProcess& src_process = *client_thread->GetOwnerProcess();
- KProcess& dst_process = *server_thread->GetOwnerProcess();
- UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess());
-
- auto* src_msg_buffer = memory.GetPointer<u32>(client_message);
- auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
- std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
-
- // Translate special header ad-hoc.
- // TODO: fix this mess
- MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
- MessageBuffer::MessageHeader src_header(src_msg);
- MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
- if (src_header.GetHasSpecialHeader()) {
- MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
- Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread,
- dst_msg, src_msg, src_special_header);
- if (R_FAILED(res)) {
- CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
- }
- }
+ // If the client is closed, we're always signaled.
+ if (m_parent->IsClientClosed()) {
+ return true;
}
- // We succeeded.
- R_SUCCEED();
+ // Otherwise, we're signaled if we have a request and aren't handling one.
+ return !m_request_list.empty() && m_current_request == nullptr;
}
void KServerSession::CleanupRequests() {
@@ -527,31 +1397,30 @@ void KServerSession::CleanupRequests() {
SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request.
- // const uintptr_t client_message = request->GetAddress();
- // const size_t client_buffer_size = request->GetSize();
+ const uint64_t client_message = request->GetAddress();
+ const size_t client_buffer_size = request->GetSize();
KThread* client_thread = request->GetThread();
KEvent* event = request->GetEvent();
- // KProcess *server_process = request->GetServerProcess();
- // KProcess *client_process = (client_thread != nullptr) ?
- // client_thread->GetOwnerProcess() : nullptr;
- // KProcessPageTable *client_page_table = (client_process != nullptr) ?
- // std::addressof(client_process->GetPageTable())
- // : nullptr;
+ KProcess* server_process = request->GetServerProcess();
+ KProcess* client_process =
+ (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
+ KProcessPageTable* client_page_table =
+ (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
// Cleanup the mappings.
- // Result result = CleanupMap(request, server_process, client_page_table);
+ Result result = CleanupMap(request, server_process, client_page_table);
// If there's a client thread, update it.
if (client_thread != nullptr) {
if (event != nullptr) {
- // // We need to reply async.
- // ReplyAsyncError(client_process, client_message, client_buffer_size,
- // (R_SUCCEEDED(result) ? ResultSessionClosed : result));
+ // We need to reply async.
+ ReplyAsyncError(client_process, client_message, client_buffer_size,
+ (R_SUCCEEDED(result) ? ResultSessionClosed : result));
- // // Unlock the client buffer.
+ // Unlock the client buffer.
// NOTE: Nintendo does not check the result of this.
- // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
+ client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
@@ -567,4 +1436,97 @@ void KServerSession::CleanupRequests() {
}
}
+void KServerSession::OnClientClosed() {
+ KScopedLightLock lk{m_lock};
+
+ // Handle any pending requests.
+ KSessionRequest* prev_request = nullptr;
+ while (true) {
+ // Declare variables for processing the request.
+ KSessionRequest* request = nullptr;
+ KEvent* event = nullptr;
+ KThread* thread = nullptr;
+ bool cur_request = false;
+ bool terminate = false;
+
+ // Get the next request.
+ {
+ KScopedSchedulerLock sl{m_kernel};
+
+ if (m_current_request != nullptr && m_current_request != prev_request) {
+ // Set the request, open a reference as we process it.
+ request = m_current_request;
+ request->Open();
+ cur_request = true;
+
+ // Get thread and event for the request.
+ thread = request->GetThread();
+ event = request->GetEvent();
+
+ // If the thread is terminating, handle that.
+ if (thread->IsTerminationRequested()) {
+ request->ClearThread();
+ request->ClearEvent();
+ terminate = true;
+ }
+
+ prev_request = request;
+ } else if (!m_request_list.empty()) {
+ // Pop the request from the front of the list.
+ request = std::addressof(m_request_list.front());
+ m_request_list.pop_front();
+
+ // Get thread and event for the request.
+ thread = request->GetThread();
+ event = request->GetEvent();
+ }
+ }
+
+ // If there are no requests, we're done.
+ if (request == nullptr) {
+ break;
+ }
+
+ // All requests must have threads.
+ ASSERT(thread != nullptr);
+
+ // Ensure that we close the request when done.
+ SCOPE_EXIT({ request->Close(); });
+
+ // If we're terminating, close a reference to the thread and event.
+ if (terminate) {
+ thread->Close();
+ if (event != nullptr) {
+ event->Close();
+ }
+ }
+
+ // If we need to, reply.
+ if (event != nullptr && !cur_request) {
+ // There must be no mappings.
+ ASSERT(request->GetSendCount() == 0);
+ ASSERT(request->GetReceiveCount() == 0);
+ ASSERT(request->GetExchangeCount() == 0);
+
+ // Get the process and page table.
+ KProcess* client_process = thread->GetOwnerProcess();
+ auto& client_pt = client_process->GetPageTable();
+
+ // Reply to the request.
+ ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
+ ResultSessionClosed);
+
+ // Unlock the buffer.
+ // NOTE: Nintendo does not check the result of this.
+ client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
+
+ // Signal the event.
+ event->Signal();
+ }
+ }
+
+ // Notify.
+ this->NotifyAvailable(ResultSessionClosed);
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 403891919..2876c231b 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -49,14 +49,21 @@ public:
bool IsSignaled() const override;
void OnClientClosed();
- /// TODO: flesh these out to match the real kernel
Result OnRequest(KSessionRequest* request);
- Result SendReply(bool is_hle = false);
- Result ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
+ Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
+ KPhysicalAddress server_message_paddr, bool is_hle = false);
+ Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
+ KPhysicalAddress server_message_paddr,
+ std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
std::weak_ptr<Service::SessionRequestManager> manager = {});
Result SendReplyHLE() {
- return SendReply(true);
+ R_RETURN(this->SendReply(0, 0, 0, true));
+ }
+
+ Result ReceiveRequestHLE(std::shared_ptr<Service::HLERequestContext>* out_context,
+ std::weak_ptr<Service::SessionRequestManager> manager) {
+ R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager));
}
private:
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index 44d7a8f02..4a1f6027e 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
m_name = name;
// Set our owner process.
- //! FIXME: this is the wrong process!
- m_process = m_kernel.ApplicationProcess();
+ m_process = GetCurrentProcessPointer(m_kernel);
m_process->Open();
// Set our port.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 7d9a6e9cf..7d3421929 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -1258,11 +1258,11 @@ ThreadState KThread::RequestTerminate() {
// Change the thread's priority to be higher than any system thread's.
this->IncreaseBasePriority(TerminatingThreadPriority);
- // If the thread is runnable, send a termination interrupt to other cores.
+ // If the thread is runnable, send a termination interrupt to cores it may be running on.
if (this->GetState() == ThreadState::Runnable) {
- if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask() &
- ~(1ULL << GetCurrentCoreId(m_kernel));
- core_mask != 0) {
+ // NOTE: We do not mask the "current core", because this code may not actually be
+ // executing from the thread representing the "current core".
+ if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) {
Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask);
}
}
@@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
}
Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
- // TODO: per-process memory
- return kernel.System().ApplicationMemory();
+ return GetCurrentProcess(kernel).GetMemory();
}
KScopedDisableDispatch::~KScopedDisableDispatch() {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e9925d231..f13e232b2 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -314,11 +314,7 @@ public:
m_current_core_id = core;
}
- KProcess* GetOwnerProcess() {
- return m_parent;
- }
-
- const KProcess* GetOwnerProcess() const {
+ KProcess* GetOwnerProcess() const {
return m_parent;
}
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index 8a0b08761..530b45218 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -5,6 +5,7 @@
#include <optional>
+#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e479dacde..1030f0c12 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -68,8 +68,6 @@ struct KernelCore::Impl {
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
- global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
- global_handle_table->Initialize(KHandleTable::MaxTableSize);
is_phantom_mode_for_singlecore = false;
@@ -121,13 +119,8 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIdMin;
next_thread_id = 1;
- global_handle_table->Finalize();
- global_handle_table.reset();
-
preemption_event = nullptr;
- exclusive_monitor.reset();
-
// Cleanup persistent kernel objects
auto CleanupObject = [](KAutoObject* obj) {
if (obj) {
@@ -191,8 +184,6 @@ struct KernelCore::Impl {
}
void InitializePhysicalCores() {
- exclusive_monitor =
- Core::MakeExclusiveMonitor(system.ApplicationMemory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
const s32 core{static_cast<s32>(i)};
@@ -247,7 +238,7 @@ struct KernelCore::Impl {
void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent(
"PreemptionCallback",
- [this, &kernel](std::uintptr_t, s64 time,
+ [this, &kernel](s64 time,
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
{
KScopedSchedulerLock lock(kernel);
@@ -791,10 +782,6 @@ struct KernelCore::Impl {
std::shared_ptr<Core::Timing::EventType> preemption_event;
- // This is the kernel's handle table or supervisor handle table which
- // stores all the objects in place.
- std::unique_ptr<KHandleTable> global_handle_table;
-
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
@@ -805,7 +792,6 @@ struct KernelCore::Impl {
std::mutex server_lock;
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
- std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
@@ -882,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
return impl->system_resource_limit;
}
-KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
- return impl->global_handle_table->GetObject<KThread>(handle);
-}
-
void KernelCore::AppendNewProcess(KProcess* process) {
impl->process_list.push_back(process);
}
@@ -959,14 +941,6 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer;
}
-Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
- return *impl->exclusive_monitor;
-}
-
-const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
- return *impl->exclusive_monitor;
-}
-
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
return *impl->global_object_list_container;
}
@@ -1030,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
return impl->next_user_process_id++;
}
-KHandleTable& KernelCore::GlobalHandleTable() {
- return *impl->global_handle_table;
-}
-
-const KHandleTable& KernelCore::GlobalHandleTable() const {
- return *impl->global_handle_table;
-}
-
void KernelCore::RegisterCoreThread(std::size_t core_id) {
impl->RegisterCoreThread(core_id);
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 78c88902c..5d4102145 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -116,9 +116,6 @@ public:
/// Retrieves a shared pointer to the system resource limit instance.
KResourceLimit* GetSystemResourceLimit();
- /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
- KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
-
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(KProcess* process);
@@ -170,10 +167,6 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id);
- Core::ExclusiveMonitor& GetExclusiveMonitor();
-
- const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
-
KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer& ObjectListContainer() const;
diff --git a/src/core/hle/kernel/message_buffer.h b/src/core/hle/kernel/message_buffer.h
index 75b275310..d528a9bb3 100644
--- a/src/core/hle/kernel/message_buffer.h
+++ b/src/core/hle/kernel/message_buffer.h
@@ -18,13 +18,13 @@ public:
static constexpr inline u64 NullTag = 0;
public:
- enum class ReceiveListCountType : u32 {
- None = 0,
- ToMessageBuffer = 1,
- ToSingleBuffer = 2,
+ enum ReceiveListCountType : u32 {
+ ReceiveListCountType_None = 0,
+ ReceiveListCountType_ToMessageBuffer = 1,
+ ReceiveListCountType_ToSingleBuffer = 2,
- CountOffset = 2,
- CountMax = 13,
+ ReceiveListCountType_CountOffset = 2,
+ ReceiveListCountType_CountMax = 13,
};
private:
@@ -591,16 +591,16 @@ public:
// Add the size of the receive list.
const auto count = hdr.GetReceiveListCount();
switch (count) {
- case MessageHeader::ReceiveListCountType::None:
+ case MessageHeader::ReceiveListCountType_None:
break;
- case MessageHeader::ReceiveListCountType::ToMessageBuffer:
+ case MessageHeader::ReceiveListCountType_ToMessageBuffer:
break;
- case MessageHeader::ReceiveListCountType::ToSingleBuffer:
+ case MessageHeader::ReceiveListCountType_ToSingleBuffer:
msg_size += ReceiveListEntry::GetDataSize();
break;
default:
msg_size += (static_cast<s32>(count) -
- static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) *
+ static_cast<s32>(MessageHeader::ReceiveListCountType_CountOffset)) *
ReceiveListEntry::GetDataSize();
break;
}
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index ada998772..231e4d0e1 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -118,7 +118,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED();
case InfoType::IsApplication:
- LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
*result = process->IsApplication();
R_SUCCEED();
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 47a3e7bb0..85cc4f561 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -48,8 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
};
// Send the reply.
- R_TRY(session->SendReply());
- // R_TRY(session->SendReply(message, buffer_size, message_paddr));
+ R_TRY(session->SendReply(message, buffer_size, message_paddr));
}
// Receive a message.
@@ -85,8 +84,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
if (R_SUCCEEDED(result)) {
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
if (session != nullptr) {
- // result = session->ReceiveRequest(message, buffer_size, message_paddr);
- result = session->ReceiveRequest();
+ result = session->ReceiveRequest(message, buffer_size, message_paddr);
if (ResultNotFound == result) {
continue;
}
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index e1ad78607..38e71d516 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -38,7 +38,9 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
+constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
+constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
} // namespace Kernel
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a266d7c21..97eb56ff0 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
return;
}
- auto transfer_mem =
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
+ auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
}
std::vector<u8> memory(transfer_mem->GetSize());
- system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
- memory.size());
+ ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
return;
}
- auto transfer_mem =
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
+ auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
}
std::vector<u8> memory(transfer_mem->GetSize());
- system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
- memory.size());
+ ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 23e56c77a..bd4ca753b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
return;
}
- const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
- auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
- auto transfer_memory{
- process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
+ auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
+ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
const auto session_id{impl->GetSessionId()};
if (session_id == -1) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 6a7bf9416..91f33aabd 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
auto params = rp.PopRaw<OpusParameters>();
auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
- auto transfer_memory{
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- transfer_memory_handle)};
+ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
params.sample_rate, params.channel_count, transfer_memory_size);
@@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
- auto transfer_memory{
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- transfer_memory_handle)};
+ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
LOG_DEBUG(Service_Audio,
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
@@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
auto params = rp.PopRaw<OpusParametersEx>();
auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
- auto transfer_memory{
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- transfer_memory_handle)};
+ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
params.sample_rate, params.channel_count, transfer_memory_size);
@@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
- auto transfer_memory{
- system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- transfer_memory_handle)};
+ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
LOG_DEBUG(Service_Audio,
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index fe2ed8df8..31da86074 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
crash_report += "\nBacktrace:\n";
- for (size_t i = 0; i < info.backtrace_size; i++) {
+ for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp
index c8e74c764..b4ff663c2 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.cpp
+++ b/src/core/hle/service/hid/controllers/applet_resource.cpp
@@ -4,7 +4,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/hid/controllers/applet_resource.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
namespace Service::HID {
@@ -164,6 +164,22 @@ Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_mem
return ResultSuccess;
}
+AruidData* AppletResource::GetAruidData(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index == AruidIndexMax) {
+ return nullptr;
+ }
+ return &data[aruid_index];
+}
+
+AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) {
+ return &data[aruid_index];
+}
+
+bool AppletResource::IsVibrationAruidActive(u64 aruid) const {
+ return aruid == 0 || aruid == active_vibration_aruid;
+}
+
u64 AppletResource::GetIndexFromAruid(u64 aruid) {
for (std::size_t i = 0; i < AruidIndexMax; i++) {
if (registration_list.flag[i] == RegistrationStatus::Initialized &&
diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h
index e7991f93a..0862fdc2f 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.h
+++ b/src/core/hle/service/hid/controllers/applet_resource.h
@@ -4,6 +4,7 @@
#pragma once
#include <array>
+#include <mutex>
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -20,6 +21,60 @@ class KSharedMemory;
namespace Service::HID {
struct SharedMemoryFormat;
+class AppletResource;
+class NPadResource;
+
+static constexpr std::size_t AruidIndexMax = 0x20;
+static constexpr u64 SystemAruid = 0;
+
+enum class RegistrationStatus : u32 {
+ None,
+ Initialized,
+ PendingDelete,
+};
+
+struct DataStatusFlag {
+ union {
+ u32 raw{};
+
+ BitField<0, 1, u32> is_initialized;
+ BitField<1, 1, u32> is_assigned;
+ BitField<16, 1, u32> enable_pad_input;
+ BitField<17, 1, u32> enable_six_axis_sensor;
+ BitField<18, 1, u32> bit_18;
+ BitField<19, 1, u32> is_palma_connectable;
+ BitField<20, 1, u32> enable_palma_boost_mode;
+ BitField<21, 1, u32> enable_touchscreen;
+ };
+};
+
+struct AruidRegisterList {
+ std::array<RegistrationStatus, AruidIndexMax> flag{};
+ std::array<u64, AruidIndexMax> aruid{};
+};
+static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
+
+struct AruidData {
+ DataStatusFlag flag{};
+ u64 aruid{};
+ SharedMemoryFormat* shared_memory_format{nullptr};
+};
+
+struct HandheldConfig {
+ bool is_handheld_hid_enabled;
+ bool is_force_handheld;
+ bool is_joycon_rail_enabled;
+ bool is_force_handheld_style_vibration;
+};
+static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size");
+
+struct AppletResourceHolder {
+ std::shared_ptr<AppletResource> applet_resource{nullptr};
+ std::recursive_mutex* shared_mutex{nullptr};
+ NPadResource* shared_npad_resource{nullptr};
+ std::shared_ptr<HandheldConfig> handheld_config{nullptr};
+ long* handle_1;
+};
class AppletResource {
public:
@@ -36,6 +91,10 @@ public:
u64 GetActiveAruid();
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
+ AruidData* GetAruidData(u64 aruid);
+ AruidData* GetAruidDataByIndex(std::size_t aruid_index);
+
+ bool IsVibrationAruidActive(u64 aruid) const;
u64 GetIndexFromAruid(u64 aruid);
@@ -52,46 +111,12 @@ public:
Result UnregisterCoreAppletResource();
private:
- static constexpr std::size_t AruidIndexMax = 0x20;
-
- enum RegistrationStatus : u32 {
- None,
- Initialized,
- PendingDelete,
- };
-
- struct DataStatusFlag {
- union {
- u32 raw{};
-
- BitField<0, 1, u32> is_initialized;
- BitField<1, 1, u32> is_assigned;
- BitField<16, 1, u32> enable_pad_input;
- BitField<17, 1, u32> enable_six_axis_sensor;
- BitField<18, 1, u32> bit_18;
- BitField<19, 1, u32> is_palma_connectable;
- BitField<20, 1, u32> enable_palma_boost_mode;
- BitField<21, 1, u32> enable_touchscreen;
- };
- };
-
- struct AruidRegisterList {
- std::array<RegistrationStatus, AruidIndexMax> flag{};
- std::array<u64, AruidIndexMax> aruid{};
- };
- static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
-
- struct AruidData {
- DataStatusFlag flag{};
- u64 aruid{};
- SharedMemoryFormat* shared_memory_format{nullptr};
- };
-
u64 active_aruid{};
AruidRegisterList registration_list{};
std::array<AruidData, AruidIndexMax> data{};
std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
s32 ref_counter{};
+ u64 active_vibration_aruid;
Core::System& system;
};
diff --git a/src/core/hle/service/hid/controllers/capture_button.cpp b/src/core/hle/service/hid/controllers/capture_button.cpp
new file mode 100644
index 000000000..7847c080e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/capture_button.cpp
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/capture_button.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+
+namespace Service::HID {
+
+CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+
+CaptureButton::~CaptureButton() = default;
+
+void CaptureButton::OnInit() {}
+
+void CaptureButton::OnRelease() {}
+
+void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!smart_update) {
+ return;
+ }
+
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ auto& header = data->shared_memory_format->capture_button.header;
+ header.timestamp = core_timing.GetGlobalTimeNs().count();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/capture_button.h
index d2052fb17..dcc4715c5 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/capture_button.h
@@ -6,12 +6,11 @@
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
-struct CommonHeader;
-class Controller_Stubbed final : public ControllerBase {
+class CaptureButton final : public ControllerBase {
public:
- explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header);
- ~Controller_Stubbed() override;
+ explicit CaptureButton(Core::HID::HIDCore& hid_core_);
+ ~CaptureButton() override;
// Called when the controller is initialized
void OnInit() override;
@@ -23,7 +22,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
- CommonHeader& header;
bool smart_update{};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
index 3961d2b5f..4b574c2e5 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -5,13 +5,11 @@
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_six_axis.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
-ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
- ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
- : ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
+ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
console = hid_core.GetEmulatedConsole();
}
@@ -22,6 +20,16 @@ void ConsoleSixAxis::OnInit() {}
void ConsoleSixAxis::OnRelease() {}
void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console;
+
if (!IsControllerActivated()) {
return;
}
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
index 3d1c9ce23..e3351f83c 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.h
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -10,12 +10,9 @@ class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
-struct ConsoleSixAxisSensorSharedMemoryFormat;
-
class ConsoleSixAxis final : public ControllerBase {
public:
- explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
- ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
+ explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
~ConsoleSixAxis() override;
// Called when the controller is initialized
@@ -28,7 +25,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
- ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 0bcd87062..afca7154c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -31,4 +31,11 @@ void ControllerBase::DeactivateController() {
bool ControllerBase::IsControllerActivated() const {
return is_activated;
}
+
+void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource,
+ std::recursive_mutex* resource_mutex) {
+ applet_resource = resource;
+ shared_mutex = resource_mutex;
+}
+
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 4326c7821..b34b85ece 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -3,8 +3,11 @@
#pragma once
+#include <memory>
+
#include "common/common_types.h"
#include "core/hle/result.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
namespace Core::Timing {
class CoreTiming;
@@ -12,7 +15,7 @@ class CoreTiming;
namespace Core::HID {
class HIDCore;
-}
+} // namespace Core::HID
namespace Service::HID {
class ControllerBase {
@@ -39,8 +42,13 @@ public:
bool IsControllerActivated() const;
+ void SetAppletResource(std::shared_ptr<AppletResource> resource,
+ std::recursive_mutex* resource_mutex);
+
protected:
bool is_activated{false};
+ std::shared_ptr<AppletResource> applet_resource{nullptr};
+ std::recursive_mutex* shared_mutex{nullptr};
Core::HID::HIDCore& hid_core;
};
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.cpp b/src/core/hle/service/hid/controllers/debug_mouse.cpp
new file mode 100644
index 000000000..ceeb78d36
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.cpp
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/frontend/emu_window.h"
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/debug_mouse.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+
+namespace Service::HID {
+
+DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
+ emulated_devices = hid_core.GetEmulatedDevices();
+}
+
+DebugMouse::~DebugMouse() = default;
+
+void DebugMouse::OnInit() {}
+void DebugMouse::OnRelease() {}
+
+void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse;
+
+ if (!IsControllerActivated()) {
+ shared_memory.mouse_lifo.buffer_count = 0;
+ shared_memory.mouse_lifo.buffer_tail = 0;
+ return;
+ }
+
+ next_state = {};
+
+ const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
+ next_state.sampling_number = last_entry.sampling_number + 1;
+
+ if (Settings::values.mouse_enabled) {
+ const auto& mouse_button_state = emulated_devices->GetMouseButtons();
+ const auto& mouse_position_state = emulated_devices->GetMousePosition();
+ const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
+ next_state.attribute.is_connected.Assign(1);
+ next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
+ next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
+ next_state.delta_x = next_state.x - last_entry.x;
+ next_state.delta_y = next_state.y - last_entry.y;
+ next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
+ next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
+
+ last_mouse_wheel_state = mouse_wheel_state;
+ next_state.button = mouse_button_state;
+ }
+
+ shared_memory.mouse_lifo.WriteNextEntry(next_state);
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.h b/src/core/hle/service/hid/controllers/debug_mouse.h
new file mode 100644
index 000000000..ec939fa9f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.h
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Core::HID {
+class EmulatedDevices;
+struct MouseState;
+struct AnalogStickState;
+} // namespace Core::HID
+
+namespace Service::HID {
+class DebugMouse final : public ControllerBase {
+public:
+ explicit DebugMouse(Core::HID::HIDCore& hid_core_);
+ ~DebugMouse() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+private:
+ Core::HID::MouseState next_state{};
+ Core::HID::AnalogStickState last_mouse_wheel_state{};
+ Core::HID::EmulatedDevices* emulated_devices = nullptr;
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 7d2370b4f..dc83f90f3 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,14 +6,13 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
-DebugPad::DebugPad(Core::HID::HIDCore& hid_core_,
- DebugPadSharedMemoryFormat& debug_pad_shared_memory)
- : ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
+DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
@@ -24,6 +23,16 @@ void DebugPad::OnInit() {}
void DebugPad::OnRelease() {}
void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad;
+
if (!IsControllerActivated()) {
shared_memory.debug_pad_lifo.buffer_count = 0;
shared_memory.debug_pad_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 8ab29eca8..dd00b2402 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,12 +15,9 @@ class CoreTiming;
}
namespace Service::HID {
-struct DebugPadSharedMemoryFormat;
-
class DebugPad final : public ControllerBase {
public:
- explicit DebugPad(Core::HID::HIDCore& hid_core_,
- DebugPadSharedMemoryFormat& debug_pad_shared_memory);
+ explicit DebugPad(Core::HID::HIDCore& hid_core_);
~DebugPad() override;
// Called when the controller is initialized
@@ -34,7 +31,6 @@ public:
private:
DebugPadState next_state{};
- DebugPadSharedMemoryFormat& shared_memory;
Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.cpp b/src/core/hle/service/hid/controllers/digitizer.cpp
new file mode 100644
index 000000000..d5514c965
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.cpp
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/digitizer.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+
+namespace Service::HID {
+
+Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+
+Digitizer::~Digitizer() = default;
+
+void Digitizer::OnInit() {}
+
+void Digitizer::OnRelease() {}
+
+void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!smart_update) {
+ return;
+ }
+
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ auto& header = data->shared_memory_format->digitizer.header;
+ header.timestamp = core_timing.GetGlobalTimeNs().count();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.h b/src/core/hle/service/hid/controllers/digitizer.h
new file mode 100644
index 000000000..d81f814c3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+
+class Digitizer final : public ControllerBase {
+public:
+ explicit Digitizer(Core::HID::HIDCore& hid_core_);
+ ~Digitizer() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+private:
+ bool smart_update{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index f658005f6..c73da13ee 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -6,8 +6,9 @@
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/gesture.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
@@ -21,24 +22,42 @@ constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
-Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory)
- : ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
+Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
console = hid_core.GetEmulatedConsole();
}
Gesture::~Gesture() = default;
void Gesture::OnInit() {
- shared_memory.gesture_lifo.buffer_count = 0;
- shared_memory.gesture_lifo.buffer_tail = 0;
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ shared_memory = &data->shared_memory_format->gesture;
+ shared_memory->gesture_lifo.buffer_count = 0;
+ shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true;
}
void Gesture::OnRelease() {}
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ shared_memory = &data->shared_memory_format->gesture;
+
if (!IsControllerActivated()) {
- shared_memory.gesture_lifo.buffer_count = 0;
- shared_memory.gesture_lifo.buffer_tail = 0;
+ shared_memory->gesture_lifo.buffer_count = 0;
+ shared_memory->gesture_lifo.buffer_tail = 0;
return;
}
@@ -46,7 +65,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
- static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) /
+ static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
(1000 * 1000 * 1000);
// Only update if necessary
@@ -54,7 +73,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
- last_update_timestamp = shared_memory.gesture_lifo.timestamp;
+ last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
@@ -97,7 +116,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
- const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
@@ -127,7 +146,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
next_state.points = gesture.points;
last_gesture = gesture;
- shared_memory.gesture_lifo.WriteNextEntry(next_state);
+ shared_memory->gesture_lifo.WriteNextEntry(next_state);
}
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -300,7 +319,7 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
}
const GestureState& Gesture::GetLastGestureEntry() const {
- return shared_memory.gesture_lifo.ReadCurrentEntry().state;
+ return shared_memory->gesture_lifo.ReadCurrentEntry().state;
}
GestureProperties Gesture::GetGestureProperties() {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 41fdfcd03..78da1552a 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -18,8 +18,7 @@ struct GestureSharedMemoryFormat;
class Gesture final : public ControllerBase {
public:
- explicit Gesture(Core::HID::HIDCore& hid_core_,
- GestureSharedMemoryFormat& gesture_shared_memory);
+ explicit Gesture(Core::HID::HIDCore& hid_core_);
~Gesture() override;
// Called when the controller is initialized
@@ -74,7 +73,7 @@ private:
GestureProperties GetGestureProperties();
GestureState next_state{};
- GestureSharedMemoryFormat& shared_memory;
+ GestureSharedMemoryFormat* shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/home_button.cpp b/src/core/hle/service/hid/controllers/home_button.cpp
new file mode 100644
index 000000000..1397379f3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.cpp
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/home_button.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+
+namespace Service::HID {
+
+HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+
+HomeButton::~HomeButton() = default;
+
+void HomeButton::OnInit() {}
+
+void HomeButton::OnRelease() {}
+
+void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!smart_update) {
+ return;
+ }
+
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ auto& header = data->shared_memory_format->home_button.header;
+ header.timestamp = core_timing.GetGlobalTimeNs().count();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/home_button.h b/src/core/hle/service/hid/controllers/home_button.h
new file mode 100644
index 000000000..e91c2aa5d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+
+class HomeButton final : public ControllerBase {
+public:
+ explicit HomeButton(Core::HID::HIDCore& hid_core_);
+ ~HomeButton() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+private:
+ bool smart_update{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 871e5036a..c069bcbb2 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -5,14 +5,13 @@
#include "core/core_timing.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/keyboard.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
-Keyboard::Keyboard(Core::HID::HIDCore& hid_core_,
- KeyboardSharedMemoryFormat& keyboard_shared_memory)
- : ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
+Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -23,6 +22,16 @@ void Keyboard::OnInit() {}
void Keyboard::OnRelease() {}
void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard;
+
if (!IsControllerActivated()) {
shared_memory.keyboard_lifo.buffer_count = 0;
shared_memory.keyboard_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 4d72171b9..e8ca326c6 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -7,12 +7,9 @@
#include "core/hle/service/hid/controllers/types/keyboard_types.h"
namespace Service::HID {
-struct KeyboardSharedMemoryFormat;
-
class Keyboard final : public ControllerBase {
public:
- explicit Keyboard(Core::HID::HIDCore& hid_core_,
- KeyboardSharedMemoryFormat& keyboard_shared_memory);
+ explicit Keyboard(Core::HID::HIDCore& hid_core_);
~Keyboard() override;
// Called when the controller is initialized
@@ -26,7 +23,6 @@ public:
private:
KeyboardState next_state{};
- KeyboardSharedMemoryFormat& shared_memory;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index de5b2c804..3a8d1751b 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,13 +5,13 @@
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/mouse.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
-Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory)
- : ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
+Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -21,6 +21,16 @@ void Mouse::OnInit() {}
void Mouse::OnRelease() {}
void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse;
+
if (!IsControllerActivated()) {
shared_memory.mouse_lifo.buffer_count = 0;
shared_memory.mouse_lifo.buffer_tail = 0;
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 363f316a5..cefad956c 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -12,11 +12,9 @@ struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
-struct MouseSharedMemoryFormat;
-
class Mouse final : public ControllerBase {
public:
- explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory);
+ explicit Mouse(Core::HID::HIDCore& hid_core_);
~Mouse() override;
// Called when the controller is initialized
@@ -31,7 +29,6 @@ public:
private:
Core::HID::MouseState next_state{};
Core::HID::AnalogStickState last_mouse_wheel_state{};
- MouseSharedMemoryFormat& shared_memory;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 53a737cf5..17cd0d7a0 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -16,46 +16,102 @@
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/npad.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
-constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
- Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
- Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
- Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
- Core::HID::NpadIdType::Handheld,
-};
-
-NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
- KernelHelpers::ServiceContext& service_context_)
- : ControllerBase{hid_core_}, service_context{service_context_} {
- for (std::size_t i = 0; i < controller_data.size(); ++i) {
- auto& controller = controller_data[i];
- controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state;
- controller.device = hid_core.GetEmulatedControllerByIndex(i);
- controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
- Core::HID::DEFAULT_VIBRATION_VALUE;
- controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
- Core::HID::DEFAULT_VIBRATION_VALUE;
- Core::HID::ControllerUpdateCallback engine_callback{
- .on_change = [this,
- i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
- .is_npad_service = true,
- };
- controller.callback_key = controller.device->SetCallback(engine_callback);
+
+NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
+ : hid_core{hid_core_}, service_context{service_context_}, npad_resource{service_context} {
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
+ for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
+ auto& controller = controller_data[aruid_index][i];
+ controller.device = hid_core.GetEmulatedControllerByIndex(i);
+ controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
+ Core::HID::DEFAULT_VIBRATION_VALUE;
+ controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex]
+ .latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change =
+ [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
+ .is_npad_service = true,
+ };
+ controller.callback_key = controller.device->SetCallback(engine_callback);
+ }
}
}
NPad::~NPad() {
- for (std::size_t i = 0; i < controller_data.size(); ++i) {
- auto& controller = controller_data[i];
- controller.device->DeleteCallback(controller.callback_key);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
+ for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
+ auto& controller = controller_data[aruid_index][i];
+ controller.device->DeleteCallback(controller.callback_key);
+ }
+ }
+}
+
+Result NPad::Activate() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadResourceOverflow;
+ }
+
+ if (ref_counter == 0) {
+ std::scoped_lock lock{mutex};
+
+ // TODO: Activate handlers and AbstractedPad
}
- OnRelease();
+
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NPad::Activate(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex};
+
+ auto* data = applet_resource_holder.applet_resource->GetAruidData(aruid);
+ const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return ResultSuccess;
+ }
+
+ for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
+ auto& controller = controller_data[aruid_index][i];
+ controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
+ }
+
+ // Prefill controller buffers
+ for (auto& controller : controller_data[aruid_index]) {
+ auto* npad = controller.shared_memory;
+ npad->fullkey_color = {
+ .attribute = ColorAttribute::NoController,
+ .fullkey = {},
+ };
+ npad->joycon_color = {
+ .attribute = ColorAttribute::NoController,
+ .left = {},
+ .right = {},
+ };
+ // HW seems to initialize the first 19 entries
+ for (std::size_t i = 0; i < 19; ++i) {
+ WriteEmptyEntry(npad);
+ }
+ }
+
+ return ResultSuccess;
+}
+
+Result NPad::ActivateNpadResource() {
+ return npad_resource.Activate();
+}
+
+Result NPad::ActivateNpadResource(u64 aruid) {
+ return npad_resource.Activate(aruid);
}
void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
@@ -64,41 +120,50 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
return;
}
- if (controller_idx >= controller_data.size()) {
- return;
- }
- auto& controller = controller_data[controller_idx];
- const auto is_connected = controller.device->IsConnected();
- const auto npad_type = controller.device->GetNpadStyleIndex();
- const auto npad_id = controller.device->GetNpadIdType();
- switch (type) {
- case Core::HID::ControllerTriggerType::Connected:
- case Core::HID::ControllerTriggerType::Disconnected:
- if (is_connected == controller.is_connected) {
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ if (controller_idx >= controller_data[aruid_index].size()) {
return;
}
- UpdateControllerAt(npad_type, npad_id, is_connected);
- break;
- case Core::HID::ControllerTriggerType::Battery: {
- if (!controller.device->IsConnected()) {
- return;
+
+ auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
+
+ if (!data->flag.is_assigned) {
+ continue;
+ }
+
+ auto& controller = controller_data[aruid_index][controller_idx];
+ const auto is_connected = controller.device->IsConnected();
+ const auto npad_type = controller.device->GetNpadStyleIndex();
+ const auto npad_id = controller.device->GetNpadIdType();
+ switch (type) {
+ case Core::HID::ControllerTriggerType::Connected:
+ case Core::HID::ControllerTriggerType::Disconnected:
+ if (is_connected == controller.is_connected) {
+ return;
+ }
+ UpdateControllerAt(data->aruid, npad_type, npad_id, is_connected);
+ break;
+ case Core::HID::ControllerTriggerType::Battery: {
+ if (!controller.device->IsConnected()) {
+ return;
+ }
+ auto* shared_memory = controller.shared_memory;
+ const auto& battery_level = controller.device->GetBattery();
+ shared_memory->battery_level_dual = battery_level.dual.battery_level;
+ shared_memory->battery_level_left = battery_level.left.battery_level;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
+ break;
+ }
+ default:
+ break;
}
- auto* shared_memory = controller.shared_memory;
- const auto& battery_level = controller.device->GetBattery();
- shared_memory->battery_level_dual = battery_level.dual.battery_level;
- shared_memory->battery_level_left = battery_level.left.battery_level;
- shared_memory->battery_level_right = battery_level.right.battery_level;
- break;
- }
- default:
- break;
}
}
-void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
- auto& controller = GetControllerFromNpadIdType(npad_id);
- if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
+void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
+ if (!npad_resource.IsControllerSupported(aruid, controller.device->GetNpadStyleIndex())) {
return;
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
@@ -107,7 +172,7 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
const auto& battery_level = controller.device->GetBattery();
auto* shared_memory = controller.shared_memory;
if (controller_type == Core::HID::NpadStyleIndex::None) {
- controller.styleset_changed_event->Signal();
+ npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
return;
}
@@ -291,45 +356,11 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
Common::Input::PollingMode::Active);
}
- SignalStyleSetChangedEvent(npad_id);
+ npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
}
-void NPad::OnInit() {
- if (!IsControllerActivated()) {
- return;
- }
-
- for (std::size_t i = 0; i < controller_data.size(); ++i) {
- auto& controller = controller_data[i];
- controller.styleset_changed_event =
- service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
- }
-
- supported_npad_id_types.resize(npad_id_list.size());
- std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
- npad_id_list.size() * sizeof(Core::HID::NpadIdType));
-
- // Prefill controller buffers
- for (auto& controller : controller_data) {
- auto* npad = controller.shared_memory;
- npad->fullkey_color = {
- .attribute = ColorAttribute::NoController,
- .fullkey = {},
- };
- npad->joycon_color = {
- .attribute = ColorAttribute::NoController,
- .left = {},
- .right = {},
- };
- // HW seems to initialize the first 19 entries
- for (std::size_t i = 0; i < 19; ++i) {
- WriteEmptyEntry(npad);
- }
- }
-}
-
void NPad::WriteEmptyEntry(NpadInternalState* npad) {
NPadGenericState dummy_pad_state{};
NpadGcTriggerState dummy_gc_state{};
@@ -351,31 +382,20 @@ void NPad::WriteEmptyEntry(NpadInternalState* npad) {
npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
}
-void NPad::OnRelease() {
- is_controller_initialized = false;
- for (std::size_t i = 0; i < controller_data.size(); ++i) {
- auto& controller = controller_data[i];
- service_context.CloseEvent(controller.styleset_changed_event);
- for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
- VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
- }
- }
-}
-
-void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
- std::scoped_lock lock{mutex};
- auto& controller = GetControllerFromNpadIdType(npad_id);
+void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) {
+ std::scoped_lock lock{*applet_resource_holder.shared_mutex};
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
if (!controller.device->IsConnected() && controller.is_connected) {
- DisconnectNpad(npad_id);
+ DisconnectNpad(aruid, npad_id);
return;
}
if (!controller.device->IsConnected()) {
return;
}
if (controller.device->IsConnected() && !controller.is_connected) {
- InitNewlyAddedController(npad_id);
+ InitNewlyAddedController(aruid, npad_id);
}
// This function is unique to yuzu for the turbo buttons and motion to work properly
@@ -432,222 +452,232 @@ void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
}
void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
- if (!IsControllerActivated()) {
+ if (ref_counter == 0) {
return;
}
- for (std::size_t i = 0; i < controller_data.size(); ++i) {
- auto& controller = controller_data[i];
- auto* npad = controller.shared_memory;
-
- const auto& controller_type = controller.device->GetNpadStyleIndex();
+ std::scoped_lock lock{*applet_resource_holder.shared_mutex};
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
+ const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
+ const auto aruid = data->aruid;
- if (controller_type == Core::HID::NpadStyleIndex::None ||
- !controller.device->IsConnected()) {
+ if (!data->flag.is_assigned) {
continue;
}
- RequestPadStateUpdate(controller.device->GetNpadIdType());
- auto& pad_state = controller.npad_pad_state;
- auto& libnx_state = controller.npad_libnx_state;
- auto& trigger_state = controller.npad_trigger_state;
-
- // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
- // any controllers.
- libnx_state.connection_status.raw = 0;
- libnx_state.connection_status.is_connected.Assign(1);
- switch (controller_type) {
- case Core::HID::NpadStyleIndex::None:
- ASSERT(false);
- break;
- case Core::HID::NpadStyleIndex::ProController:
- case Core::HID::NpadStyleIndex::NES:
- case Core::HID::NpadStyleIndex::SNES:
- case Core::HID::NpadStyleIndex::N64:
- case Core::HID::NpadStyleIndex::SegaGenesis:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_wired.Assign(1);
-
- libnx_state.connection_status.is_wired.Assign(1);
- pad_state.sampling_number =
- npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->fullkey_lifo.WriteNextEntry(pad_state);
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_wired.Assign(1);
- pad_state.connection_status.is_left_connected.Assign(1);
- pad_state.connection_status.is_right_connected.Assign(1);
- pad_state.connection_status.is_left_wired.Assign(1);
- pad_state.connection_status.is_right_wired.Assign(1);
-
- libnx_state.connection_status.is_wired.Assign(1);
- libnx_state.connection_status.is_left_connected.Assign(1);
- libnx_state.connection_status.is_right_connected.Assign(1);
- libnx_state.connection_status.is_left_wired.Assign(1);
- libnx_state.connection_status.is_right_wired.Assign(1);
- pad_state.sampling_number =
- npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->handheld_lifo.WriteNextEntry(pad_state);
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- if (controller.is_dual_left_connected) {
+ for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
+ auto& controller = controller_data[aruid_index][i];
+ controller.shared_memory =
+ &data->shared_memory_format->npad.npad_entry[i].internal_state;
+ auto* npad = controller.shared_memory;
+
+ const auto& controller_type = controller.device->GetNpadStyleIndex();
+
+ if (controller_type == Core::HID::NpadStyleIndex::None ||
+ !controller.device->IsConnected()) {
+ continue;
+ }
+
+ RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());
+ auto& pad_state = controller.npad_pad_state;
+ auto& libnx_state = controller.npad_libnx_state;
+ auto& trigger_state = controller.npad_trigger_state;
+
+ // LibNX exclusively uses this section, so we always update it since LibNX doesn't
+ // activate any controllers.
+ libnx_state.connection_status.raw = 0;
+ libnx_state.connection_status.is_connected.Assign(1);
+ switch (controller_type) {
+ case Core::HID::NpadStyleIndex::None:
+ ASSERT(false);
+ break;
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::NES:
+ case Core::HID::NpadStyleIndex::SNES:
+ case Core::HID::NpadStyleIndex::N64:
+ case Core::HID::NpadStyleIndex::SegaGenesis:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
+ pad_state.sampling_number =
+ npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->fullkey_lifo.WriteNextEntry(pad_state);
+ break;
+ case Core::HID::NpadStyleIndex::Handheld:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
pad_state.connection_status.is_left_connected.Assign(1);
+ pad_state.connection_status.is_right_connected.Assign(1);
+ pad_state.connection_status.is_left_wired.Assign(1);
+ pad_state.connection_status.is_right_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
libnx_state.connection_status.is_left_connected.Assign(1);
- }
- if (controller.is_dual_right_connected) {
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_left_wired.Assign(1);
+ libnx_state.connection_status.is_right_wired.Assign(1);
+ pad_state.sampling_number =
+ npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->handheld_lifo.WriteNextEntry(pad_state);
+ break;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ if (controller.is_dual_left_connected) {
+ pad_state.connection_status.is_left_connected.Assign(1);
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ pad_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ }
+
+ pad_state.sampling_number =
+ npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_dual_lifo.WriteNextEntry(pad_state);
+ break;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_left_connected.Assign(1);
+
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ pad_state.sampling_number =
+ npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_left_lifo.WriteNextEntry(pad_state);
+ break;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
pad_state.connection_status.is_right_connected.Assign(1);
+
libnx_state.connection_status.is_right_connected.Assign(1);
+ pad_state.sampling_number =
+ npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_right_lifo.WriteNextEntry(pad_state);
+ break;
+ case Core::HID::NpadStyleIndex::GameCube:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.connection_status.is_wired.Assign(1);
+
+ libnx_state.connection_status.is_wired.Assign(1);
+ pad_state.sampling_number =
+ npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ trigger_state.sampling_number =
+ npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->fullkey_lifo.WriteNextEntry(pad_state);
+ npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
+ break;
+ case Core::HID::NpadStyleIndex::Pokeball:
+ pad_state.connection_status.raw = 0;
+ pad_state.connection_status.is_connected.Assign(1);
+ pad_state.sampling_number =
+ npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->palma_lifo.WriteNextEntry(pad_state);
+ break;
+ default:
+ break;
}
- pad_state.sampling_number =
- npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->joy_dual_lifo.WriteNextEntry(pad_state);
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_left_connected.Assign(1);
-
- libnx_state.connection_status.is_left_connected.Assign(1);
- pad_state.sampling_number =
- npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->joy_left_lifo.WriteNextEntry(pad_state);
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_right_connected.Assign(1);
-
- libnx_state.connection_status.is_right_connected.Assign(1);
- pad_state.sampling_number =
- npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->joy_right_lifo.WriteNextEntry(pad_state);
- break;
- case Core::HID::NpadStyleIndex::GameCube:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_wired.Assign(1);
-
- libnx_state.connection_status.is_wired.Assign(1);
- pad_state.sampling_number =
- npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
- trigger_state.sampling_number =
- npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->fullkey_lifo.WriteNextEntry(pad_state);
- npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
- break;
- case Core::HID::NpadStyleIndex::Pokeball:
- pad_state.connection_status.raw = 0;
- pad_state.connection_status.is_connected.Assign(1);
- pad_state.sampling_number =
- npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad->palma_lifo.WriteNextEntry(pad_state);
- break;
- default:
- break;
- }
+ libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
+ libnx_state.l_stick = pad_state.l_stick;
+ libnx_state.r_stick = pad_state.r_stick;
+ npad->system_ext_lifo.WriteNextEntry(pad_state);
- libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
- libnx_state.l_stick = pad_state.l_stick;
- libnx_state.r_stick = pad_state.r_stick;
- npad->system_ext_lifo.WriteNextEntry(pad_state);
-
- press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
+ press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
+ }
}
}
-void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
- hid_core.SetSupportedStyleTag(style_set);
-
- if (is_controller_initialized) {
- return;
+Result NPad::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set) {
+ std::scoped_lock lock{mutex};
+ hid_core.SetSupportedStyleTag({supported_style_set});
+ const Result result = npad_resource.SetSupportedNpadStyleSet(aruid, supported_style_set);
+ if (result.IsSuccess()) {
+ OnUpdate({});
}
-
- // Once SetSupportedStyleSet is called controllers are fully initialized
- is_controller_initialized = true;
+ return result;
}
-Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
- if (!is_controller_initialized) {
- return {Core::HID::NpadStyleSet::None};
+Result NPad::GetSupportedNpadStyleSet(u64 aruid,
+ Core::HID::NpadStyleSet& out_supported_style_set) const {
+ std::scoped_lock lock{mutex};
+ const Result result = npad_resource.GetSupportedNpadStyleSet(out_supported_style_set, aruid);
+
+ if (result == ResultUndefinedStyleset) {
+ out_supported_style_set = Core::HID::NpadStyleSet::None;
+ return ResultSuccess;
}
- return hid_core.GetSupportedStyleTag();
+
+ return result;
}
-Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
- constexpr std::size_t max_number_npad_ids = 0xa;
- const auto length = data.size();
- ASSERT(length > 0 && (length % sizeof(u32)) == 0);
- const std::size_t elements = length / sizeof(u32);
+Result NPad::GetMaskedSupportedNpadStyleSet(
+ u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const {
+ std::scoped_lock lock{mutex};
+ const Result result =
+ npad_resource.GetMaskedSupportedNpadStyleSet(out_supported_style_set, aruid);
- if (elements > max_number_npad_ids) {
- return InvalidArraySize;
+ if (result == ResultUndefinedStyleset) {
+ out_supported_style_set = Core::HID::NpadStyleSet::None;
+ return ResultSuccess;
}
- supported_npad_id_types.clear();
- supported_npad_id_types.resize(elements);
- std::memcpy(supported_npad_id_types.data(), data.data(), length);
- return ResultSuccess;
+ return result;
}
-void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
- const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
- ASSERT(max_length <= copy_amount);
- std::memcpy(data, supported_npad_id_types.data(), copy_amount);
-}
+Result NPad::SetSupportedNpadIdType(u64 aruid,
+ std::span<const Core::HID::NpadIdType> supported_npad_list) {
+ std::scoped_lock lock{mutex};
+ if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
+ return ResultInvalidArraySize;
+ }
-std::size_t NPad::GetSupportedNpadIdTypesSize() const {
- return supported_npad_id_types.size();
-}
+ Result result = npad_resource.SetSupportedNpadIdType(aruid, supported_npad_list);
-void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
- if (joy_hold_type != NpadJoyHoldType::Horizontal &&
- joy_hold_type != NpadJoyHoldType::Vertical) {
- LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
- joy_hold_type);
- return;
+ if (result.IsSuccess()) {
+ OnUpdate({});
}
- hold_type = joy_hold_type;
-}
-NpadJoyHoldType NPad::GetHoldType() const {
- return hold_type;
+ return result;
}
-void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
- if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
- ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
- return;
- }
-
- handheld_activation_mode = activation_mode;
+Result NPad::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
+ std::scoped_lock lock{mutex};
+ return npad_resource.SetNpadJoyHoldType(aruid, hold_type);
}
-NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
- return handheld_activation_mode;
+Result NPad::GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const {
+ std::scoped_lock lock{mutex};
+ return npad_resource.GetNpadJoyHoldType(out_hold_type, aruid);
}
-void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
- communication_mode = communication_mode_;
+Result NPad::SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode) {
+ std::scoped_lock lock{mutex};
+ Result result = npad_resource.SetNpadHandheldActivationMode(aruid, mode);
+ if (result.IsSuccess()) {
+ OnUpdate({});
+ }
+ return result;
}
-NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
- return communication_mode;
+Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const {
+ std::scoped_lock lock{mutex};
+ return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid);
}
-bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
+bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
return false;
}
- auto& controller = GetControllerFromNpadIdType(npad_id);
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (controller.shared_memory->assignment_mode != assignment_mode) {
controller.shared_memory->assignment_mode = assignment_mode;
}
@@ -658,17 +688,17 @@ bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
- DisconnectNpad(npad_id);
+ DisconnectNpad(aruid, npad_id);
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return false;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
- DisconnectNpad(npad_id);
+ DisconnectNpad(aruid, npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return false;
}
return false;
@@ -682,37 +712,38 @@ bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
- DisconnectNpad(npad_id);
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ DisconnectNpad(aruid, npad_id);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
return false;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
- DisconnectNpad(npad_id);
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ DisconnectNpad(aruid, npad_id);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
return false;
}
// We have two controllers connected to the same npad_id we need to split them
new_npad_id = hid_core.GetFirstDisconnectedNpadId();
- auto& controller_2 = GetControllerFromNpadIdType(new_npad_id);
- DisconnectNpad(npad_id);
+ auto& controller_2 = GetControllerFromNpadIdType(aruid, new_npad_id);
+ DisconnectNpad(aruid, npad_id);
if (npad_device_type == NpadJoyDeviceType::Left) {
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
controller_2.is_dual_left_connected = false;
controller_2.is_dual_right_connected = true;
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
} else {
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
controller_2.is_dual_left_connected = true;
controller_2.is_dual_right_connected = false;
- UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
+ UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
}
return true;
}
-bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
+bool NPad::VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
+ std::size_t device_index,
const Core::HID::VibrationValue& vibration_value) {
- auto& controller = GetControllerFromNpadIdType(npad_id);
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (!controller.device->IsConnected()) {
return false;
}
@@ -755,7 +786,8 @@ bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t d
return controller.device->SetVibration(device_index, vibration);
}
-void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
+void NPad::VibrateController(u64 aruid,
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value) {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return;
@@ -765,7 +797,7 @@ void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_d
return;
}
- auto& controller = GetControllerFromHandle(vibration_device_handle);
+ auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
@@ -795,14 +827,14 @@ void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_d
return;
}
- if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
+ if (VibrateControllerAtIndex(aruid, controller.device->GetNpadIdType(), device_index,
vibration_value)) {
controller.vibration[device_index].latest_vibration_value = vibration_value;
}
}
void NPad::VibrateControllers(
- std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
+ u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
@@ -814,17 +846,17 @@ void NPad::VibrateControllers(
"this is undefined behavior!");
for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
- VibrateController(vibration_device_handles[i], vibration_values[i]);
+ VibrateController(aruid, vibration_device_handles[i], vibration_values[i]);
}
}
Core::HID::VibrationValue NPad::GetLastVibration(
- const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
+ u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return {};
}
- const auto& controller = GetControllerFromHandle(vibration_device_handle);
+ const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return controller.vibration[device_index].latest_vibration_value;
}
@@ -835,14 +867,15 @@ void NPad::InitializeVibrationDevice(
return;
}
+ const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
- InitializeVibrationDeviceAtIndex(npad_index, device_index);
+ InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index);
}
-void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
+void NPad::InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
std::size_t device_index) {
- auto& controller = GetControllerFromNpadIdType(npad_id);
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (!Settings::values.vibration_enabled.GetValue()) {
controller.vibration[device_index].device_mounted = false;
return;
@@ -857,60 +890,50 @@ void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
}
bool NPad::IsVibrationDeviceMounted(
- const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
+ u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
return false;
}
- const auto& controller = GetControllerFromHandle(vibration_device_handle);
+ const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return controller.vibration[device_index].device_mounted;
}
-Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
- if (!IsNpadIdValid(npad_id)) {
- LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- // Fallback to player 1
- const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
- return controller.styleset_changed_event->GetReadableEvent();
- }
-
- const auto& controller = GetControllerFromNpadIdType(npad_id);
- return controller.styleset_changed_event->GetReadableEvent();
-}
-
-void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
- const auto& controller = GetControllerFromNpadIdType(npad_id);
- controller.styleset_changed_event->Signal();
+Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
+ Core::HID::NpadIdType npad_id) {
+ std::scoped_lock lock{mutex};
+ return npad_resource.AcquireNpadStyleSetUpdateEventHandle(aruid, out_event, npad_id);
}
-void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
- UpdateControllerAt(controller, npad_id, true);
+void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
+ Core::HID::NpadIdType npad_id) {
+ UpdateControllerAt(aruid, controller, npad_id, true);
}
-void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
- bool connected) {
- auto& controller = GetControllerFromNpadIdType(npad_id);
+void NPad::UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex type,
+ Core::HID::NpadIdType npad_id, bool connected) {
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
if (!connected) {
- DisconnectNpad(npad_id);
+ DisconnectNpad(aruid, npad_id);
return;
}
controller.device->SetNpadStyleIndex(type);
- InitNewlyAddedController(npad_id);
+ InitNewlyAddedController(aruid, npad_id);
}
-Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
+Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return InvalidNpadId;
+ return ResultInvalidNpadId;
}
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
- auto& controller = GetControllerFromNpadIdType(npad_id);
+ auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
// Send an empty vibration to stop any vibrations.
- VibrateControllerAtIndex(npad_id, device_idx, {});
+ VibrateControllerAtIndex(aruid, npad_id, device_idx, {});
controller.vibration[device_idx].device_mounted = false;
}
@@ -944,71 +967,48 @@ Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
controller.is_dual_right_connected = true;
controller.is_connected = false;
controller.device->Disconnect();
- SignalStyleSetChangedEvent(npad_id);
+ npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
WriteEmptyEntry(shared_memory);
return ResultSuccess;
}
Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_firmware_available) const {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
- const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ const auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
return ResultSuccess;
}
Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
return is_valid;
}
- auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
sixaxis_properties.is_newly_assigned.Assign(0);
return ResultSuccess;
}
-NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
-}
-
-NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
-}
-
-NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
-}
-
-NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
-}
-
-NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
-}
-
-NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
- return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
-}
-
-Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return InvalidNpadId;
+ return ResultInvalidNpadId;
}
- auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
- auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1);
+ auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2);
auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
@@ -1055,51 +1055,62 @@ Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
}
// Disconnect the joycons and connect them as dual joycon at the first index.
- DisconnectNpad(npad_id_1);
- DisconnectNpad(npad_id_2);
+ DisconnectNpad(aruid, npad_id_1);
+ DisconnectNpad(aruid, npad_id_2);
controller_1.is_dual_left_connected = true;
controller_1.is_dual_right_connected = true;
- AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ AddNewControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
return ResultSuccess;
}
-void NPad::StartLRAssignmentMode() {
- // Nothing internally is used for lr assignment mode. Since we have the ability to set the
- // controller types from boot, it doesn't really matter about showing a selection screen
- is_in_lr_assignment_mode = true;
+Result NPad::StartLrAssignmentMode(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ bool is_enabled{};
+ Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
+ if (result.IsSuccess() && is_enabled == false) {
+ result = npad_resource.SetLrAssignmentMode(aruid, true);
+ }
+ return result;
}
-void NPad::StopLRAssignmentMode() {
- is_in_lr_assignment_mode = false;
+Result NPad::StopLrAssignmentMode(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ bool is_enabled{};
+ Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
+ if (result.IsSuccess() && is_enabled == true) {
+ result = npad_resource.SetLrAssignmentMode(aruid, false);
+ }
+ return result;
}
-Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
+Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return InvalidNpadId;
+ return ResultInvalidNpadId;
}
if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
npad_id_2 == Core::HID::NpadIdType::Other) {
return ResultSuccess;
}
- const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
- const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
+ const auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1).device;
+ const auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2).device;
const auto type_index_1 = controller_1->GetNpadStyleIndex();
const auto type_index_2 = controller_2->GetNpadStyleIndex();
const auto is_connected_1 = controller_1->IsConnected();
const auto is_connected_2 = controller_2->IsConnected();
- if (!IsControllerSupported(type_index_1) && is_connected_1) {
- return NpadNotConnected;
+ if (!npad_resource.IsControllerSupported(aruid, type_index_1) && is_connected_1) {
+ return ResultNpadNotConnected;
}
- if (!IsControllerSupported(type_index_2) && is_connected_2) {
- return NpadNotConnected;
+ if (!npad_resource.IsControllerSupported(aruid, type_index_2) && is_connected_2) {
+ return ResultNpadNotConnected;
}
- UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
- UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
+ UpdateControllerAt(aruid, type_index_2, npad_id_1, is_connected_2);
+ UpdateControllerAt(aruid, type_index_1, npad_id_2, is_connected_1);
return ResultSuccess;
}
@@ -1107,68 +1118,68 @@ Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::Npad
Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return InvalidNpadId;
+ return ResultInvalidNpadId;
}
- const auto& controller = GetControllerFromNpadIdType(npad_id).device;
+ const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
+ const auto& controller = GetControllerFromNpadIdType(aruid, npad_id).device;
pattern = controller->GetLedPattern();
return ResultSuccess;
}
-Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
- bool& is_valid) const {
- if (!IsNpadIdValid(npad_id)) {
- LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return InvalidNpadId;
- }
- const auto& controller = GetControllerFromNpadIdType(npad_id);
- is_valid = controller.unintended_home_button_input_protection;
- return ResultSuccess;
+Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
+ Core::HID::NpadIdType npad_id) const {
+ std::scoped_lock lock{mutex};
+ return npad_resource.GetHomeProtectionEnabled(out_is_enabled, aruid, npad_id);
}
-Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id) {
- if (!IsNpadIdValid(npad_id)) {
- LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return InvalidNpadId;
- }
- auto& controller = GetControllerFromNpadIdType(npad_id);
- controller.unintended_home_button_input_protection = is_protection_enabled;
- return ResultSuccess;
+Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
+ bool is_enabled) {
+ std::scoped_lock lock{mutex};
+ return npad_resource.SetHomeProtectionEnabled(aruid, npad_id, is_enabled);
}
-void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
- analog_stick_use_center_clamp = use_center_clamp;
+void NPad::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
+ std::scoped_lock lock{mutex};
+ npad_resource.SetNpadAnalogStickUseCenterClamp(aruid, is_enabled);
}
void NPad::ClearAllConnectedControllers() {
- for (auto& controller : controller_data) {
- if (controller.device->IsConnected() &&
- controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
- controller.device->Disconnect();
- controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ for (auto& controller : controller_data[aruid_index]) {
+ if (controller.device->IsConnected() &&
+ controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
+ controller.device->Disconnect();
+ controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+ }
}
}
}
void NPad::DisconnectAllConnectedControllers() {
- for (auto& controller : controller_data) {
- controller.device->Disconnect();
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ for (auto& controller : controller_data[aruid_index]) {
+ controller.device->Disconnect();
+ }
}
}
void NPad::ConnectAllDisconnectedControllers() {
- for (auto& controller : controller_data) {
- if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
- !controller.device->IsConnected()) {
- controller.device->Connect();
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ for (auto& controller : controller_data[aruid_index]) {
+ if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
+ !controller.device->IsConnected()) {
+ controller.device->Connect();
+ }
}
}
}
void NPad::ClearAllControllers() {
- for (auto& controller : controller_data) {
- controller.device->Disconnect();
- controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ for (auto& controller : controller_data[aruid_index]) {
+ controller.device->Disconnect();
+ controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
+ }
}
}
@@ -1176,128 +1187,105 @@ Core::HID::NpadButton NPad::GetAndResetPressState() {
return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
}
-void NPad::ApplyNpadSystemCommonPolicy() {
- Core::HID::NpadStyleTag styletag{};
- styletag.fullkey.Assign(1);
- styletag.handheld.Assign(1);
- styletag.joycon_dual.Assign(1);
- styletag.system_ext.Assign(1);
- styletag.system.Assign(1);
- SetSupportedStyleSet(styletag);
-
- SetNpadHandheldActivationMode(NpadHandheldActivationMode::Dual);
-
- supported_npad_id_types.clear();
- supported_npad_id_types.resize(10);
- supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
- supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
- supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
- supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
- supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
- supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
- supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
- supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
- supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
- supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
-}
-
-bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
- if (controller == Core::HID::NpadStyleIndex::Handheld) {
- const bool support_handheld =
- std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
- Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
- // Handheld is not even a supported type, lets stop here
- if (!support_handheld) {
- return false;
- }
- // Handheld shouldn't be supported in docked mode
- if (Settings::IsDockedMode()) {
- return false;
- }
+Result NPad::ApplyNpadSystemCommonPolicy(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, false);
+ if (result.IsSuccess()) {
+ OnUpdate({});
+ }
+ return result;
+}
- return true;
- }
-
- if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
- [](Core::HID::NpadIdType npad_id) {
- return npad_id <= Core::HID::NpadIdType::Player8;
- })) {
- Core::HID::NpadStyleTag style = GetSupportedStyleSet();
- switch (controller) {
- case Core::HID::NpadStyleIndex::ProController:
- return style.fullkey.As<bool>();
- case Core::HID::NpadStyleIndex::JoyconDual:
- return style.joycon_dual.As<bool>();
- case Core::HID::NpadStyleIndex::JoyconLeft:
- return style.joycon_left.As<bool>();
- case Core::HID::NpadStyleIndex::JoyconRight:
- return style.joycon_right.As<bool>();
- case Core::HID::NpadStyleIndex::GameCube:
- return style.gamecube.As<bool>();
- case Core::HID::NpadStyleIndex::Pokeball:
- return style.palma.As<bool>();
- case Core::HID::NpadStyleIndex::NES:
- return style.lark.As<bool>();
- case Core::HID::NpadStyleIndex::SNES:
- return style.lucia.As<bool>();
- case Core::HID::NpadStyleIndex::N64:
- return style.lagoon.As<bool>();
- case Core::HID::NpadStyleIndex::SegaGenesis:
- return style.lager.As<bool>();
- default:
- return false;
- }
+Result NPad::ApplyNpadSystemCommonPolicyFull(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, true);
+ if (result.IsSuccess()) {
+ OnUpdate({});
}
+ return result;
+}
+
+Result NPad::ClearNpadSystemCommonPolicy(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ const Result result = npad_resource.ClearNpadSystemCommonPolicy(aruid);
+ if (result.IsSuccess()) {
+ OnUpdate({});
+ }
+ return result;
+}
+
+void NPad::SetRevision(u64 aruid, NpadRevision revision) {
+ npad_resource.SetNpadRevision(aruid, revision);
+}
+
+NpadRevision NPad::GetRevision(u64 aruid) {
+ return npad_resource.GetNpadRevision(aruid);
+}
+
+Result NPad::RegisterAppletResourceUserId(u64 aruid) {
+ return npad_resource.RegisterAppletResourceUserId(aruid);
+}
+
+void NPad::UnregisterAppletResourceUserId(u64 aruid) {
+ npad_resource.UnregisterAppletResourceUserId(aruid);
+}
- return false;
+void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
+ std::recursive_mutex* shared_mutex) {
+ applet_resource_holder.applet_resource = resource;
+ applet_resource_holder.shared_mutex = shared_mutex;
+ applet_resource_holder.shared_npad_resource = &npad_resource;
}
NPad::NpadControllerData& NPad::GetControllerFromHandle(
- const Core::HID::VibrationDeviceHandle& device_handle) {
+ u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
- return GetControllerFromNpadIdType(npad_id);
+ return GetControllerFromNpadIdType(aruid, npad_id);
}
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
- const Core::HID::VibrationDeviceHandle& device_handle) const {
+ u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
- return GetControllerFromNpadIdType(npad_id);
+ return GetControllerFromNpadIdType(aruid, npad_id);
}
NPad::NpadControllerData& NPad::GetControllerFromHandle(
- const Core::HID::SixAxisSensorHandle& device_handle) {
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
- return GetControllerFromNpadIdType(npad_id);
+ return GetControllerFromNpadIdType(aruid, npad_id);
}
const NPad::NpadControllerData& NPad::GetControllerFromHandle(
- const Core::HID::SixAxisSensorHandle& device_handle) const {
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const {
const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
- return GetControllerFromNpadIdType(npad_id);
+ return GetControllerFromNpadIdType(aruid, npad_id);
}
-NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
+NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(u64 aruid,
+ Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1;
}
const auto npad_index = NpadIdTypeToIndex(npad_id);
- return controller_data[npad_index];
+ const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
+ return controller_data[aruid_index][npad_index];
}
const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
- Core::HID::NpadIdType npad_id) const {
+ u64 aruid, Core::HID::NpadIdType npad_id) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
npad_id = Core::HID::NpadIdType::Player1;
}
const auto npad_index = NpadIdTypeToIndex(npad_id);
- return controller_data[npad_index];
+ const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
+ return controller_data[aruid_index][npad_index];
}
Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
- auto& controller = GetControllerFromHandle(sixaxis_handle);
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
@@ -1319,8 +1307,8 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
}
const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
- const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
+ const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
switch (sixaxis_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Pokeball:
@@ -1342,7 +1330,8 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
}
AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
- const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
+ const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
+ const auto& shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory;
return {
.ui_variant = 0,
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 4e2412356..8ab333064 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,6 +11,7 @@
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/controllers/npad/npad_resource.h"
#include "core/hle/service/hid/controllers/types/npad_types.h"
namespace Core::HID {
@@ -30,111 +31,121 @@ class ServiceContext;
union Result;
namespace Service::HID {
+class AppletResource;
struct NpadInternalState;
struct NpadSixAxisSensorLifo;
struct NpadSharedMemoryFormat;
-class NPad final : public ControllerBase {
+class NPad final {
public:
- explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
- KernelHelpers::ServiceContext& service_context_);
- ~NPad() override;
+ explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
+ ~NPad();
- // Called when the controller is initialized
- void OnInit() override;
+ Result Activate();
+ Result Activate(u64 aruid);
- // When the controller is released
- void OnRelease() override;
+ Result ActivateNpadResource();
+ Result ActivateNpadResource(u64 aruid);
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing);
- void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
- Core::HID::NpadStyleTag GetSupportedStyleSet() const;
+ Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set);
+ Result GetSupportedNpadStyleSet(u64 aruid,
+ Core::HID::NpadStyleSet& out_supported_style_set) const;
+ Result GetMaskedSupportedNpadStyleSet(u64 aruid,
+ Core::HID::NpadStyleSet& out_supported_style_set) const;
- Result SetSupportedNpadIdTypes(std::span<const u8> data);
- void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
- std::size_t GetSupportedNpadIdTypesSize() const;
+ Result SetSupportedNpadIdType(u64 aruid,
+ std::span<const Core::HID::NpadIdType> supported_npad_list);
- void SetHoldType(NpadJoyHoldType joy_hold_type);
- NpadJoyHoldType GetHoldType() const;
+ Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
+ Result GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const;
- void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
- NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
+ Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode);
+ Result GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const;
- void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
- NpadCommunicationMode GetNpadCommunicationMode() const;
-
- bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
+ bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
- bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
+ bool VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
+ std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
- void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
+ void VibrateController(u64 aruid,
+ const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value);
void VibrateControllers(
- std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
+ u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values);
Core::HID::VibrationValue GetLastVibration(
- const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
+ u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
- void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
+ void InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
+ std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
bool IsVibrationDeviceMounted(
- const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
+ u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
- Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
- void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
+ Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
+ Core::HID::NpadIdType npad_id);
// Adds a new controller at an index.
- void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
+ void AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
+ Core::HID::NpadIdType npad_id);
// Adds a new controller at an index with connection status.
- void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
- bool connected);
+ void UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
+ Core::HID::NpadIdType npad_id, bool connected);
- Result DisconnectNpad(Core::HID::NpadIdType npad_id);
+ Result DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id);
Result IsFirmwareUpdateAvailableForSixAxisSensor(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_firmware_available) const;
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle);
-
- NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
- NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
- NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
- NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
- NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
- NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
+ u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle);
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
- Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
- bool& is_enabled) const;
- Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id);
- void SetAnalogStickUseCenterClamp(bool use_center_clamp);
+
+ Result IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
+ Core::HID::NpadIdType npad_id) const;
+ Result EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
+ bool is_enabled);
+
+ void SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
- Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Result MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2);
- void StartLRAssignmentMode();
- void StopLRAssignmentMode();
- Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ Result StartLrAssignmentMode(u64 aruid);
+ Result StopLrAssignmentMode(u64 aruid);
+ Result SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
- void ApplyNpadSystemCommonPolicy();
+ Result ApplyNpadSystemCommonPolicy(u64 aruid);
+ Result ApplyNpadSystemCommonPolicyFull(u64 aruid);
+ Result ClearNpadSystemCommonPolicy(u64 aruid);
+
+ void SetRevision(u64 aruid, NpadRevision revision);
+ NpadRevision GetRevision(u64 aruid);
+
+ Result RegisterAppletResourceUserId(u64 aruid);
+ void UnregisterAppletResourceUserId(u64 aruid);
+ void SetNpadExternals(std::shared_ptr<AppletResource> resource,
+ std::recursive_mutex* shared_mutex);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
@@ -146,12 +157,10 @@ private:
};
struct NpadControllerData {
- Kernel::KEvent* styleset_changed_event{};
NpadInternalState* shared_memory = nullptr;
Core::HID::EmulatedController* device = nullptr;
std::array<VibrationData, 2> vibration{};
- bool unintended_home_button_input_protection{};
bool is_connected{};
// Dual joycons can have only one side connected
@@ -166,39 +175,40 @@ private:
};
void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
- void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
- bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
- void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
+ void InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id);
+ void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id);
void WriteEmptyEntry(NpadInternalState* npad);
NpadControllerData& GetControllerFromHandle(
- const Core::HID::VibrationDeviceHandle& device_handle);
+ u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
- const Core::HID::VibrationDeviceHandle& device_handle) const;
+ u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle(
- const Core::HID::SixAxisSensorHandle& device_handle);
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
- const Core::HID::SixAxisSensorHandle& device_handle) const;
- NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
- const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
+ NpadControllerData& GetControllerFromNpadIdType(u64 aruid, Core::HID::NpadIdType npad_id);
+ const NpadControllerData& GetControllerFromNpadIdType(u64 aruid,
+ Core::HID::NpadIdType npad_id) const;
Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
- const Core::HID::SixAxisSensorHandle& device_handle);
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
- const Core::HID::SixAxisSensorHandle& device_handle) const;
+ u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
- std::atomic<u64> press_state{};
-
- std::array<NpadControllerData, NpadCount> controller_data{};
+ Core::HID::HIDCore& hid_core;
KernelHelpers::ServiceContext& service_context;
- std::mutex mutex;
- std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
- NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
- NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
- NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
- bool permit_vibration_session_enabled{false};
- bool analog_stick_use_center_clamp{false};
- bool is_in_lr_assignment_mode{false};
- bool is_controller_initialized{false};
+
+ s32 ref_counter{};
+ mutable std::mutex mutex;
+ NPadResource npad_resource;
+ AppletResourceHolder applet_resource_holder{};
+ Kernel::KEvent* input_event{nullptr};
+ std::mutex* input_mutex{nullptr};
+
+ std::atomic<u64> press_state{};
+ bool permit_vibration_session_enabled;
+ std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
+ controller_data{};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_data.cpp b/src/core/hle/service/hid/controllers/npad/npad_data.cpp
new file mode 100644
index 000000000..d2423b6d3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_data.cpp
@@ -0,0 +1,228 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/controllers/npad/npad_data.h"
+#include "core/hle/service/hid/hid_util.h"
+
+namespace Service::HID {
+
+NPadData::NPadData() {
+ ClearNpadSystemCommonPolicy();
+}
+
+NPadData::~NPadData() = default;
+
+NpadStatus NPadData::GetNpadStatus() const {
+ return status;
+}
+
+void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) {
+ status.use_center_clamp.Assign(is_enabled);
+}
+
+bool NPadData::GetNpadAnalogStickUseCenterClamp() const {
+ return status.use_center_clamp.As<bool>();
+}
+
+void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
+ status.system_ext_state.Assign(is_enabled);
+}
+
+bool NPadData::GetNpadSystemExtState() const {
+ return status.system_ext_state.As<bool>();
+}
+
+Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list) {
+ // Note: Real limit is 11. But array size is 10. N's bug?
+ if (list.size() > MaxSupportedNpadIdTypes) {
+ return ResultInvalidArraySize;
+ }
+
+ supported_npad_id_types_count = list.size();
+ memcpy(supported_npad_id_types.data(), list.data(),
+ list.size() * sizeof(Core::HID::NpadIdType));
+
+ return ResultSuccess;
+}
+
+std::size_t NPadData::GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const {
+ std::size_t out_size = std::min(supported_npad_id_types_count, out_list.size());
+
+ memcpy(out_list.data(), supported_npad_id_types.data(),
+ out_size * sizeof(Core::HID::NpadIdType));
+
+ return out_size;
+}
+
+bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const {
+ for (std::size_t i = 0; i < supported_npad_id_types_count; i++) {
+ if (supported_npad_id_types[i] == npad_id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) {
+ supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ handheld_activation_mode = NpadHandheldActivationMode::Dual;
+
+ status.is_supported_styleset_set.Assign(true);
+ status.is_hold_type_set.Assign(true);
+ status.lr_assignment_mode.Assign(false);
+ status.is_policy.Assign(true);
+ if (is_full_policy) {
+ status.is_full_policy.Assign(true);
+ }
+
+ supported_npad_id_types_count = 10;
+ supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
+ supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
+ supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
+ supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
+ supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
+ supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
+ supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
+ supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
+ supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
+ supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
+
+ for (auto& input_protection : is_unintended_home_button_input_protection) {
+ input_protection = true;
+ }
+}
+
+void NPadData::ClearNpadSystemCommonPolicy() {
+ status.raw = 0;
+ supported_npad_style_set = Core::HID::NpadStyleSet::All;
+ npad_hold_type = NpadJoyHoldType::Vertical;
+ handheld_activation_mode = NpadHandheldActivationMode::Dual;
+
+ for (auto& button_assignment : npad_button_assignment) {
+ button_assignment = Core::HID::NpadButton::None;
+ }
+
+ supported_npad_id_types_count = 10;
+ supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
+ supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
+ supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
+ supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
+ supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
+ supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
+ supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
+ supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
+ supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
+ supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
+
+ for (auto& input_protection : is_unintended_home_button_input_protection) {
+ input_protection = true;
+ }
+}
+
+void NPadData::SetNpadJoyHoldType(NpadJoyHoldType hold_type) {
+ npad_hold_type = hold_type;
+ status.is_hold_type_set.Assign(true);
+}
+
+NpadJoyHoldType NPadData::GetNpadJoyHoldType() const {
+ return npad_hold_type;
+}
+
+void NPadData::SetHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
+ handheld_activation_mode = activation_mode;
+}
+
+NpadHandheldActivationMode NPadData::GetHandheldActivationMode() const {
+ return handheld_activation_mode;
+}
+
+void NPadData::SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set) {
+ supported_npad_style_set = style_set;
+ status.is_supported_styleset_set.Assign(true);
+ status.is_hold_type_set.Assign(true);
+}
+
+Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
+ return supported_npad_style_set;
+}
+
+bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const {
+ Core::HID::NpadStyleTag style = {supported_npad_style_set};
+ switch (style_index) {
+ case Core::HID::NpadStyleIndex::ProController:
+ return style.fullkey.As<bool>();
+ case Core::HID::NpadStyleIndex::Handheld:
+ return style.handheld.As<bool>();
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ return style.joycon_dual.As<bool>();
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return style.joycon_left.As<bool>();
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return style.joycon_right.As<bool>();
+ case Core::HID::NpadStyleIndex::GameCube:
+ return style.gamecube.As<bool>();
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return style.palma.As<bool>();
+ case Core::HID::NpadStyleIndex::NES:
+ return style.lark.As<bool>();
+ case Core::HID::NpadStyleIndex::SNES:
+ return style.lucia.As<bool>();
+ case Core::HID::NpadStyleIndex::N64:
+ return style.lagoon.As<bool>();
+ case Core::HID::NpadStyleIndex::SegaGenesis:
+ return style.lager.As<bool>();
+ default:
+ return false;
+ }
+}
+
+void NPadData::SetLrAssignmentMode(bool is_enabled) {
+ status.lr_assignment_mode.Assign(is_enabled);
+}
+
+bool NPadData::GetLrAssignmentMode() const {
+ return status.lr_assignment_mode.As<bool>();
+}
+
+void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
+ status.assigning_single_on_sl_sr_press.Assign(is_enabled);
+}
+
+bool NPadData::GetAssigningSingleOnSlSrPress() const {
+ return status.assigning_single_on_sl_sr_press.As<bool>();
+}
+
+void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) {
+ is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)] = is_enabled;
+}
+
+bool NPadData::GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const {
+ return is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)];
+}
+
+void NPadData::SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
+ std::size_t style_index) {
+ npad_button_assignment[style_index] = button_assignment;
+}
+
+Core::HID::NpadButton NPadData::GetCaptureButtonAssignment(std::size_t style_index) const {
+ return npad_button_assignment[style_index];
+}
+
+std::size_t NPadData::GetNpadCaptureButtonAssignmentList(
+ std::span<Core::HID::NpadButton> out_list) const {
+ for (std::size_t i = 0; i < out_list.size(); i++) {
+ Core::HID::NpadStyleSet style_set = GetStylesetByIndex(i);
+ if ((style_set & supported_npad_style_set) == Core::HID::NpadStyleSet::None ||
+ npad_button_assignment[i] == Core::HID::NpadButton::None) {
+ return i;
+ }
+ out_list[i] = npad_button_assignment[i];
+ }
+
+ return out_list.size();
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_data.h b/src/core/hle/service/hid/controllers/npad/npad_data.h
new file mode 100644
index 000000000..f799a9f9c
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_data.h
@@ -0,0 +1,88 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <span>
+
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/hid/controllers/types/npad_types.h"
+
+namespace Service::HID {
+
+struct NpadStatus {
+ union {
+ u32 raw{};
+
+ BitField<0, 1, u32> is_supported_styleset_set;
+ BitField<1, 1, u32> is_hold_type_set;
+ BitField<2, 1, u32> lr_assignment_mode;
+ BitField<3, 1, u32> assigning_single_on_sl_sr_press;
+ BitField<4, 1, u32> is_full_policy;
+ BitField<5, 1, u32> is_policy;
+ BitField<6, 1, u32> use_center_clamp;
+ BitField<7, 1, u32> system_ext_state;
+ };
+};
+static_assert(sizeof(NpadStatus) == 4, "NpadStatus is an invalid size");
+
+/// Handles Npad request from HID interfaces
+class NPadData final {
+public:
+ explicit NPadData();
+ ~NPadData();
+
+ NpadStatus GetNpadStatus() const;
+
+ void SetNpadAnalogStickUseCenterClamp(bool is_enabled);
+ bool GetNpadAnalogStickUseCenterClamp() const;
+
+ void SetNpadSystemExtStateEnabled(bool is_enabled);
+ bool GetNpadSystemExtState() const;
+
+ Result SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list);
+ std::size_t GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const;
+ bool IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const;
+
+ void SetNpadSystemCommonPolicy(bool is_full_policy);
+ void ClearNpadSystemCommonPolicy();
+
+ void SetNpadJoyHoldType(NpadJoyHoldType hold_type);
+ NpadJoyHoldType GetNpadJoyHoldType() const;
+
+ void SetHandheldActivationMode(NpadHandheldActivationMode activation_mode);
+ NpadHandheldActivationMode GetHandheldActivationMode() const;
+
+ void SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set);
+ Core::HID::NpadStyleSet GetSupportedNpadStyleSet() const;
+ bool IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const;
+
+ void SetLrAssignmentMode(bool is_enabled);
+ bool GetLrAssignmentMode() const;
+
+ void SetAssigningSingleOnSlSrPress(bool is_enabled);
+ bool GetAssigningSingleOnSlSrPress() const;
+
+ void SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id);
+ bool GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const;
+
+ void SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
+ std::size_t style_index);
+ Core::HID::NpadButton GetCaptureButtonAssignment(std::size_t style_index) const;
+ std::size_t GetNpadCaptureButtonAssignmentList(std::span<Core::HID::NpadButton> out_list) const;
+
+private:
+ NpadStatus status{};
+ Core::HID::NpadStyleSet supported_npad_style_set{Core::HID::NpadStyleSet::All};
+ NpadJoyHoldType npad_hold_type{NpadJoyHoldType::Vertical};
+ NpadHandheldActivationMode handheld_activation_mode{};
+ std::array<Core::HID::NpadIdType, MaxSupportedNpadIdTypes> supported_npad_id_types{};
+ std::array<Core::HID::NpadButton, StyleIndexCount> npad_button_assignment{};
+ std::size_t supported_npad_id_types_count{};
+ std::array<bool, MaxSupportedNpadIdTypes> is_unintended_home_button_input_protection{};
+};
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_resource.cpp b/src/core/hle/service/hid/controllers/npad/npad_resource.cpp
new file mode 100644
index 000000000..0a9341a39
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_resource.cpp
@@ -0,0 +1,685 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/controllers/npad/npad_resource.h"
+#include "core/hle/service/hid/controllers/types/npad_types.h"
+#include "core/hle/service/hid/errors.h"
+#include "core/hle/service/hid/hid_util.h"
+
+namespace Service::HID {
+
+NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {}
+
+NPadResource::~NPadResource() = default;
+
+Result NPadResource::RegisterAppletResourceUserId(u64 aruid) {
+ const auto aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index < AruidIndexMax) {
+ return ResultAruidAlreadyRegistered;
+ }
+
+ std::size_t data_index = AruidIndexMax;
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ if (!state[i].flag.is_initialized) {
+ data_index = i;
+ break;
+ }
+ }
+
+ if (data_index == AruidIndexMax) {
+ return ResultAruidNoAvailableEntries;
+ }
+
+ auto& aruid_data = state[data_index];
+
+ aruid_data.aruid = aruid;
+ aruid_data.flag.is_initialized.Assign(true);
+
+ data_index = AruidIndexMax;
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ if (registration_list.flag[i] == RegistrationStatus::Initialized) {
+ if (registration_list.aruid[i] != aruid) {
+ continue;
+ }
+ data_index = i;
+ break;
+ }
+ if (registration_list.flag[i] == RegistrationStatus::None) {
+ data_index = i;
+ break;
+ }
+ }
+
+ if (data_index == AruidIndexMax) {
+ return ResultSuccess;
+ }
+
+ registration_list.flag[data_index] = RegistrationStatus::Initialized;
+ registration_list.aruid[data_index] = aruid;
+
+ return ResultSuccess;
+}
+
+void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+
+ DestroyStyleSetUpdateEvents(aruid);
+ if (aruid_index < AruidIndexMax) {
+ state[aruid_index] = {};
+ registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
+ }
+}
+
+void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+
+ if (aruid_index >= AruidIndexMax) {
+ return;
+ }
+
+ for (auto& controller_state : state[aruid_index].controller_state) {
+ if (!controller_state.is_styleset_update_event_initialized) {
+ continue;
+ }
+ service_context.CloseEvent(controller_state.style_set_update_event);
+ controller_state.is_styleset_update_event_initialized = false;
+ }
+}
+
+Result NPadResource::Activate(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+
+ if (aruid_index >= AruidIndexMax) {
+ return ResultSuccess;
+ }
+
+ auto& state_data = state[aruid_index];
+
+ if (state_data.flag.is_assigned) {
+ return ResultAruidAlreadyRegistered;
+ }
+
+ state_data.flag.is_assigned.Assign(true);
+ state_data.data.ClearNpadSystemCommonPolicy();
+ state_data.npad_revision = NpadRevision::Revision0;
+ state_data.button_config = {};
+
+ if (active_data_aruid == aruid) {
+ default_hold_type = active_data.GetNpadJoyHoldType();
+ active_data.SetNpadJoyHoldType(default_hold_type);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::Activate() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultAppletResourceOverflow;
+ }
+ if (ref_counter == 0) {
+ RegisterAppletResourceUserId(SystemAruid);
+ Activate(SystemAruid);
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NPadResource::Deactivate() {
+ if (ref_counter == 0) {
+ return ResultAppletResourceNotInitialized;
+ }
+
+ UnregisterAppletResourceUserId(SystemAruid);
+ ref_counter--;
+ return ResultSuccess;
+}
+
+NPadData* NPadResource::GetActiveData() {
+ return &active_data;
+}
+
+u64 NPadResource::GetActiveDataAruid() {
+ return active_data_aruid;
+}
+
+void NPadResource::SetAppletResourceUserId(u64 aruid) {
+ if (active_data_aruid == aruid) {
+ return;
+ }
+
+ active_data_aruid = aruid;
+ default_hold_type = active_data.GetNpadJoyHoldType();
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+
+ if (aruid_index >= AruidIndexMax) {
+ return;
+ }
+
+ auto& data = state[aruid_index].data;
+ if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
+ data.SetNpadJoyHoldType(default_hold_type);
+ }
+
+ active_data = data;
+ if (data.GetNpadStatus().is_hold_type_set) {
+ active_data.SetNpadJoyHoldType(default_hold_type);
+ }
+}
+
+std::size_t NPadResource::GetIndexFromAruid(u64 aruid) const {
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ if (registration_list.flag[i] == RegistrationStatus::Initialized &&
+ registration_list.aruid[i] == aruid) {
+ return i;
+ }
+ }
+ return AruidIndexMax;
+}
+
+Result NPadResource::ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ data.SetNpadSystemCommonPolicy(is_full_policy);
+ data.SetNpadJoyHoldType(default_hold_type);
+ if (active_data_aruid == aruid) {
+ active_data.SetNpadSystemCommonPolicy(is_full_policy);
+ active_data.SetNpadJoyHoldType(default_hold_type);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::ClearNpadSystemCommonPolicy(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.ClearNpadSystemCommonPolicy();
+ if (active_data_aruid == aruid) {
+ active_data.ClearNpadSystemCommonPolicy();
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ data.SetSupportedNpadStyleSet(style_set);
+ if (active_data_aruid == aruid) {
+ active_data.SetSupportedNpadStyleSet(style_set);
+ active_data.SetNpadJoyHoldType(data.GetNpadJoyHoldType());
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set,
+ u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ if (!data.GetNpadStatus().is_supported_styleset_set) {
+ return ResultUndefinedStyleset;
+ }
+
+ out_style_Set = data.GetSupportedNpadStyleSet();
+ return ResultSuccess;
+}
+
+Result NPadResource::GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set,
+ u64 aruid) const {
+ if (aruid == SystemAruid) {
+ out_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Palma |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ return ResultSuccess;
+ }
+
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ if (!data.GetNpadStatus().is_supported_styleset_set) {
+ return ResultUndefinedStyleset;
+ }
+
+ Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
+ out_style_set = data.GetSupportedNpadStyleSet();
+
+ switch (state[aruid_index].npad_revision) {
+ case NpadRevision::Revision1:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
+ Core::HID::NpadStyleSet::System;
+ break;
+ case NpadRevision::Revision2:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ break;
+ case NpadRevision::Revision3:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
+ Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
+ Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ break;
+ default:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
+ Core::HID::NpadStyleSet::System;
+ break;
+ }
+
+ out_style_set = out_style_set & mask;
+ return ResultSuccess;
+}
+
+Result NPadResource::GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ if (!data.GetNpadStatus().is_supported_styleset_set) {
+ return ResultUndefinedStyleset;
+ }
+
+ Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
+ out_style_set = data.GetSupportedNpadStyleSet();
+
+ switch (state[aruid_index].npad_revision) {
+ case NpadRevision::Revision1:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
+ Core::HID::NpadStyleSet::System;
+ break;
+ case NpadRevision::Revision2:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ break;
+ case NpadRevision::Revision3:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
+ Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
+ Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
+ Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
+ Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
+ break;
+ default:
+ mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
+ Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
+ Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
+ Core::HID::NpadStyleSet::System;
+ break;
+ }
+
+ out_style_set = out_style_set & mask;
+ return ResultSuccess;
+}
+
+NpadRevision NPadResource::GetNpadRevision(u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return NpadRevision::Revision0;
+ }
+
+ return state[aruid_index].npad_revision;
+}
+
+Result NPadResource::IsSupportedNpadStyleSet(bool& is_set, u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ is_set = state[aruid_index].data.GetNpadStatus().is_supported_styleset_set.Value() != 0;
+ return ResultSuccess;
+}
+
+Result NPadResource::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetNpadJoyHoldType(hold_type);
+ if (active_data_aruid == aruid) {
+ active_data.SetNpadJoyHoldType(hold_type);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& data = state[aruid_index].data;
+ if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
+ hold_type = active_data.GetNpadJoyHoldType();
+ return ResultSuccess;
+ }
+ hold_type = data.GetNpadJoyHoldType();
+ return ResultSuccess;
+}
+
+Result NPadResource::SetNpadHandheldActivationMode(u64 aruid,
+ NpadHandheldActivationMode activation_mode) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetHandheldActivationMode(activation_mode);
+ if (active_data_aruid == aruid) {
+ active_data.SetHandheldActivationMode(activation_mode);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
+ u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ activation_mode = state[aruid_index].data.GetHandheldActivationMode();
+ return ResultSuccess;
+}
+
+Result NPadResource::SetSupportedNpadIdType(
+ u64 aruid, std::span<const Core::HID::NpadIdType> supported_npad_list) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+ if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
+ return ResultInvalidArraySize;
+ }
+
+ Result result = state[aruid_index].data.SetSupportedNpadIdType(supported_npad_list);
+ if (result.IsSuccess() && active_data_aruid == aruid) {
+ result = active_data.SetSupportedNpadIdType(supported_npad_list);
+ }
+
+ return result;
+}
+
+bool NPadResource::IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return false;
+ }
+ return state[aruid_index].data.IsNpadStyleIndexSupported(style_index);
+}
+
+Result NPadResource::SetLrAssignmentMode(u64 aruid, bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetLrAssignmentMode(is_enabled);
+ if (active_data_aruid == aruid) {
+ active_data.SetLrAssignmentMode(is_enabled);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::GetLrAssignmentMode(bool& is_enabled, u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ is_enabled = state[aruid_index].data.GetLrAssignmentMode();
+ return ResultSuccess;
+}
+
+Result NPadResource::SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetAssigningSingleOnSlSrPress(is_enabled);
+ if (active_data_aruid == aruid) {
+ active_data.SetAssigningSingleOnSlSrPress(is_enabled);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ is_enabled = state[aruid_index].data.GetAssigningSingleOnSlSrPress();
+ return ResultSuccess;
+}
+
+Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid,
+ Kernel::KReadableEvent** out_event,
+ Core::HID::NpadIdType npad_id) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ auto& controller_state = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
+ if (!controller_state.is_styleset_update_event_initialized) {
+ // Auto clear = true
+ controller_state.style_set_update_event =
+ service_context.CreateEvent("NpadResource:StylesetUpdateEvent");
+
+ // Assume creating the event succeeds otherwise crash the system here
+ controller_state.is_styleset_update_event_initialized = true;
+ }
+
+ *out_event = &controller_state.style_set_update_event->GetReadableEvent();
+
+ if (controller_state.is_styleset_update_event_initialized) {
+ controller_state.style_set_update_event->Signal();
+ }
+
+ return ResultSuccess;
+}
+
+Result NPadResource::SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+ auto controller = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
+ if (controller.is_styleset_update_event_initialized) {
+ controller.style_set_update_event->Signal();
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
+ Core::HID::NpadIdType npad_id) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ is_enabled = state[aruid_index].data.GetHomeProtectionEnabled(npad_id);
+ return ResultSuccess;
+}
+
+Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id,
+ bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetHomeProtectionEnabled(is_enabled, npad_id);
+ if (active_data_aruid == aruid) {
+ active_data.SetHomeProtectionEnabled(is_enabled, npad_id);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
+ if (active_data_aruid == aruid) {
+ active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
+ Core::HID::NpadButton button_config) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index] = button_config;
+ return ResultSuccess;
+}
+
+Core::HID::NpadButton NPadResource::GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
+ std::size_t index, Core::HID::NpadButton mask,
+ bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return Core::HID::NpadButton::None;
+ }
+
+ auto& button_config = state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index];
+ if (is_enabled) {
+ button_config = button_config | mask;
+ return button_config;
+ }
+
+ button_config = Core::HID::NpadButton::None;
+ return Core::HID::NpadButton::None;
+}
+
+void NPadResource::ResetButtonConfig() {
+ for (auto& selected_state : state) {
+ selected_state.button_config = {};
+ }
+}
+
+Result NPadResource::SetNpadCaptureButtonAssignment(u64 aruid,
+ Core::HID::NpadStyleSet npad_style_set,
+ Core::HID::NpadButton button_assignment) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ // Must be a power of two
+ const auto raw_styleset = static_cast<u32>(npad_style_set);
+ if (raw_styleset == 0 && (raw_styleset & (raw_styleset - 1)) != 0) {
+ return ResultMultipleStyleSetSelected;
+ }
+
+ std::size_t style_index{};
+ Core::HID::NpadStyleSet style_selected{};
+ for (style_index = 0; style_index < StyleIndexCount; ++style_index) {
+ style_selected = GetStylesetByIndex(style_index);
+ if (npad_style_set == style_selected) {
+ break;
+ }
+ }
+
+ if (style_selected == Core::HID::NpadStyleSet::None) {
+ return ResultMultipleStyleSetSelected;
+ }
+
+ state[aruid_index].data.SetCaptureButtonAssignment(button_assignment, style_index);
+ if (active_data_aruid == aruid) {
+ active_data.SetCaptureButtonAssignment(button_assignment, style_index);
+ }
+ return ResultSuccess;
+}
+
+Result NPadResource::ClearNpadCaptureButtonAssignment(u64 aruid) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ for (std::size_t i = 0; i < StyleIndexCount; i++) {
+ state[aruid_index].data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
+ if (active_data_aruid == aruid) {
+ active_data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
+ }
+ }
+ return ResultSuccess;
+}
+
+std::size_t NPadResource::GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
+ u64 aruid) const {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return 0;
+ }
+ return state[aruid_index].data.GetNpadCaptureButtonAssignmentList(out_list);
+}
+
+void NPadResource::SetNpadRevision(u64 aruid, NpadRevision revision) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return;
+ }
+
+ state[aruid_index].npad_revision = revision;
+}
+
+Result NPadResource::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) {
+ const u64 aruid_index = GetIndexFromAruid(aruid);
+ if (aruid_index >= AruidIndexMax) {
+ return ResultNpadNotConnected;
+ }
+
+ state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
+ if (active_data_aruid == aruid) {
+ active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
+ }
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_resource.h b/src/core/hle/service/hid/controllers/npad/npad_resource.h
new file mode 100644
index 000000000..4c7e6ab0e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_resource.h
@@ -0,0 +1,132 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+#include <span>
+
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/npad/npad_data.h"
+#include "core/hle/service/hid/controllers/types/npad_types.h"
+#include "core/hle/service/kernel_helpers.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::HID {
+struct DataStatusFlag;
+
+struct NpadControllerState {
+ bool is_styleset_update_event_initialized{};
+ INSERT_PADDING_BYTES(0x7);
+ Kernel::KEvent* style_set_update_event{nullptr};
+ INSERT_PADDING_BYTES(0x27);
+};
+
+struct NpadState {
+ DataStatusFlag flag{};
+ u64 aruid{};
+ NPadData data{};
+ std::array<std::array<Core::HID::NpadButton, StyleIndexCount>, MaxSupportedNpadIdTypes>
+ button_config;
+ std::array<NpadControllerState, MaxSupportedNpadIdTypes> controller_state;
+ NpadRevision npad_revision;
+};
+
+/// Handles Npad request from HID interfaces
+class NPadResource final {
+public:
+ explicit NPadResource(KernelHelpers::ServiceContext& context);
+ ~NPadResource();
+
+ NPadData* GetActiveData();
+ u64 GetActiveDataAruid();
+
+ Result RegisterAppletResourceUserId(u64 aruid);
+ void UnregisterAppletResourceUserId(u64 aruid);
+
+ void DestroyStyleSetUpdateEvents(u64 aruid);
+
+ Result Activate(u64 aruid);
+ Result Activate();
+ Result Deactivate();
+
+ void SetAppletResourceUserId(u64 aruid);
+ std::size_t GetIndexFromAruid(u64 aruid) const;
+
+ Result ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy);
+ Result ClearNpadSystemCommonPolicy(u64 aruid);
+
+ Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set);
+ Result GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set, u64 aruid) const;
+ Result GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;
+ Result GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;
+
+ NpadRevision GetNpadRevision(u64 aruid) const;
+ void SetNpadRevision(u64 aruid, NpadRevision revision);
+
+ Result IsSupportedNpadStyleSet(bool& is_set, u64 aruid);
+
+ Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
+ Result GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const;
+
+ Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode activation_mode);
+ Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
+ u64 aruid) const;
+
+ Result SetSupportedNpadIdType(u64 aruid,
+ std::span<const Core::HID::NpadIdType> supported_npad_list);
+ bool IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const;
+
+ Result SetLrAssignmentMode(u64 aruid, bool is_enabled);
+ Result GetLrAssignmentMode(bool& is_enabled, u64 aruid) const;
+
+ Result SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled);
+ Result IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const;
+
+ Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
+ Core::HID::NpadIdType npad_id);
+ Result SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id);
+
+ Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
+ Core::HID::NpadIdType npad_id) const;
+ Result SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled);
+
+ Result SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);
+
+ Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
+ Core::HID::NpadButton button_config);
+ Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
+ std::size_t index, Core::HID::NpadButton mask,
+ bool is_enabled);
+ void ResetButtonConfig();
+
+ Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set,
+ Core::HID::NpadButton button_assignment);
+ Result ClearNpadCaptureButtonAssignment(u64 aruid);
+ std::size_t GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
+ u64 aruid) const;
+
+ Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled);
+
+private:
+ NPadData active_data{};
+ AruidRegisterList registration_list{};
+ std::array<NpadState, AruidIndexMax> state{};
+ u64 active_data_aruid{};
+ NpadJoyHoldType default_hold_type{};
+ s32 ref_counter{};
+
+ KernelHelpers::ServiceContext& service_context;
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
index 51581188e..0bc5169c6 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
+++ b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
@@ -3,8 +3,9 @@
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
namespace Service::HID {
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
index 36b72f9ea..adab60911 100644
--- a/src/core/hle/service/hid/controllers/six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -6,8 +6,8 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/npad.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/six_axis.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
@@ -27,14 +27,21 @@ void SixAxis::OnInit() {}
void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
+ NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
-
- const auto npad_id = IndexToNpadIdType(i);
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
@@ -50,12 +57,12 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
- auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id);
- auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id);
- auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id);
- auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id);
- auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id);
- auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id);
+ auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
+ auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
+ auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
+ auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
+ auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
+ auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
diff --git a/src/core/hle/service/hid/controllers/sleep_button.cpp b/src/core/hle/service/hid/controllers/sleep_button.cpp
new file mode 100644
index 000000000..d44b1f4cc
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.cpp
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/sleep_button.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+
+namespace Service::HID {
+
+SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+
+SleepButton::~SleepButton() = default;
+
+void SleepButton::OnInit() {}
+
+void SleepButton::OnRelease() {}
+
+void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!smart_update) {
+ return;
+ }
+
+ std::scoped_lock shared_lock{*shared_mutex};
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ auto& header = data->shared_memory_format->capture_button.header;
+ header.timestamp = core_timing.GetGlobalTimeNs().count();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/sleep_button.h b/src/core/hle/service/hid/controllers/sleep_button.h
new file mode 100644
index 000000000..59964bf63
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+
+class SleepButton final : public ControllerBase {
+public:
+ explicit SleepButton(Core::HID::HIDCore& hid_core_);
+ ~SleepButton() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+private:
+ bool smart_update{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
deleted file mode 100644
index e2a5f5d79..000000000
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/core_timing.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
-#include "core/hle/service/hid/controllers/stubbed.h"
-
-namespace Service::HID {
-
-Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
- CommonHeader& ring_lifo_header)
- : ControllerBase{hid_core_}, header{ring_lifo_header} {}
-
-Controller_Stubbed::~Controller_Stubbed() = default;
-
-void Controller_Stubbed::OnInit() {}
-
-void Controller_Stubbed::OnRelease() {}
-
-void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
- if (!smart_update) {
- return;
- }
-
- header.timestamp = core_timing.GetGlobalTimeNs().count();
- header.total_entry_count = 17;
- header.entry_count = 0;
- header.last_entry_index = 0;
-}
-
-} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 469750006..b585a5829 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -8,15 +8,14 @@
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
-TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_,
- TouchScreenSharedMemoryFormat& touch_shared_memory)
- : ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
- touchscreen_width(Layout::ScreenUndocked::Width),
+TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_)
+ : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
touchscreen_height(Layout::ScreenUndocked::Height) {
console = hid_core.GetEmulatedConsole();
}
@@ -28,6 +27,14 @@ void TouchScreen::OnInit() {}
void TouchScreen::OnRelease() {}
void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen;
shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 5b6305bfc..945d359be 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -18,8 +18,7 @@ struct TouchScreenSharedMemoryFormat;
class TouchScreen final : public ControllerBase {
public:
- explicit TouchScreen(Core::HID::HIDCore& hid_core_,
- TouchScreenSharedMemoryFormat& touch_shared_memory);
+ explicit TouchScreen(Core::HID::HIDCore& hid_core_);
~TouchScreen() override;
// Called when the controller is initialized
@@ -35,7 +34,6 @@ public:
private:
TouchScreenState next_state{};
- TouchScreenSharedMemoryFormat& shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/types/npad_types.h b/src/core/hle/service/hid/controllers/types/npad_types.h
index a5ce2562b..419c33a8c 100644
--- a/src/core/hle/service/hid/controllers/types/npad_types.h
+++ b/src/core/hle/service/hid/controllers/types/npad_types.h
@@ -9,7 +9,8 @@
#include "core/hid/hid_types.h"
namespace Service::HID {
-static constexpr std::size_t NpadCount = 10;
+static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
+static constexpr std::size_t StyleIndexCount = 7;
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
diff --git a/src/core/hle/service/hid/controllers/shared_memory_format.h b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
index 2986c113e..976043b9c 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_format.h
+++ b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
@@ -171,7 +171,7 @@ static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is
// This is nn::hid::detail::NpadSharedMemoryFormat
struct NpadSharedMemoryFormat {
- std::array<NpadSharedMemoryEntry, NpadCount> npad_entry;
+ std::array<NpadSharedMemoryEntry, MaxSupportedNpadIdTypes> npad_entry;
};
static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
"NpadSharedMemoryFormat is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/unique_pad.cpp b/src/core/hle/service/hid/controllers/unique_pad.cpp
new file mode 100644
index 000000000..6c543031d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/unique_pad.h"
+
+namespace Service::HID {
+
+UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+
+UniquePad::~UniquePad() = default;
+
+void UniquePad::OnInit() {}
+
+void UniquePad::OnRelease() {}
+
+void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!smart_update) {
+ return;
+ }
+
+ const u64 aruid = applet_resource->GetActiveAruid();
+ auto* data = applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr || !data->flag.is_assigned) {
+ return;
+ }
+
+ auto& header = data->shared_memory_format->capture_button.header;
+ header.timestamp = core_timing.GetGlobalTimeNs().count();
+ header.total_entry_count = 17;
+ header.entry_count = 0;
+ header.last_entry_index = 0;
+}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/unique_pad.h b/src/core/hle/service/hid/controllers/unique_pad.h
new file mode 100644
index 000000000..966368264
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/hid/controllers/controller_base.h"
+
+namespace Service::HID {
+
+class UniquePad final : public ControllerBase {
+public:
+ explicit UniquePad(Core::HID::HIDCore& hid_core_);
+ ~UniquePad() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+private:
+ bool smart_update{};
+};
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 6dc976fe1..bb14aa61e 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -10,15 +10,32 @@ namespace Service::HID {
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
-constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
-constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
-constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
+
+constexpr Result ResultVibrationNotInitialized{ErrorModule::HID, 121};
+constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122};
+constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123};
+constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
+constexpr Result ResultVibrationStrenghtOutOfRange{ErrorModule::HID, 126};
+constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131};
+
constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
+
+constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
+constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
+constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
+constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
+
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
-constexpr Result InvalidNpadId{ErrorModule::HID, 709};
-constexpr Result NpadNotConnected{ErrorModule::HID, 710};
-constexpr Result InvalidArraySize{ErrorModule::HID, 715};
+constexpr Result ResultNpadIsNotProController{ErrorModule::HID, 604};
+
+constexpr Result ResultInvalidNpadId{ErrorModule::HID, 709};
+constexpr Result ResultNpadNotConnected{ErrorModule::HID, 710};
+constexpr Result ResultNpadHandlerOverflow{ErrorModule::HID, 711};
+constexpr Result ResultNpadHandlerNotInitialized{ErrorModule::HID, 712};
+constexpr Result ResultInvalidArraySize{ErrorModule::HID, 715};
+constexpr Result ResultUndefinedStyleset{ErrorModule::HID, 716};
+constexpr Result ResultMultipleStyleSetSelected{ErrorModule::HID, 717};
constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041};
constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042};
@@ -27,6 +44,9 @@ constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};
+constexpr Result ResultNpadResourceOverflow{ErrorModule::HID, 2001};
+constexpr Result ResultNpadResourceNotInitialized{ErrorModule::HID, 2002};
+
constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index afbcb019f..bd2873181 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -25,6 +25,7 @@ void LoopProcess(Core::System& system) {
// TODO: Remove this hack until this service is emulated properly.
const auto process_list = system.Kernel().GetProcessList();
if (!process_list.empty()) {
+ resouce_manager->Initialize();
resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
}
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index de24b0401..a953c92b3 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -51,7 +51,7 @@ private:
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
- if (resource_manager != nullptr) {
+ if (resource_manager != nullptr && resource_manager->GetNpad()) {
resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
}
@@ -785,8 +785,8 @@ void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ct
bool is_firmware_available{};
auto controller = GetResourceManager()->GetNpad();
- controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
- is_firmware_available);
+ controller->IsFirmwareUpdateAvailableForSixAxisSensor(
+ parameters.applet_resource_user_id, parameters.sixaxis_handle, is_firmware_available);
LOG_WARNING(
Service_HID,
@@ -924,8 +924,8 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx)
const auto parameters{rp.PopRaw<Parameters>()};
auto controller = GetResourceManager()->GetNpad();
- const auto result =
- controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
+ const auto result = controller->ResetIsSixAxisSensorDeviceNewlyAssigned(
+ parameters.applet_resource_user_id, parameters.sixaxis_handle);
LOG_WARNING(
Service_HID,
@@ -970,7 +970,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- Core::HID::NpadStyleSet supported_styleset;
+ Core::HID::NpadStyleSet supported_style_set;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -978,13 +978,25 @@ void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset});
+ LOG_DEBUG(Service_HID, "called, supported_style_set={}, applet_resource_user_id={}",
+ parameters.supported_style_set, parameters.applet_resource_user_id);
+
+ const auto npad = GetResourceManager()->GetNpad();
+ const Result result = npad->SetSupportedNpadStyleSet(parameters.applet_resource_user_id,
+ parameters.supported_style_set);
- LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
- parameters.supported_styleset, parameters.applet_resource_user_id);
+ if (result.IsSuccess()) {
+ Core::HID::NpadStyleTag style_tag{parameters.supported_style_set};
+ const auto revision = npad->GetRevision(parameters.applet_resource_user_id);
+
+ if (style_tag.palma != 0 && revision < NpadRevision::Revision3) {
+ // GetResourceManager()->GetPalma()->EnableBoostMode(parameters.applet_resource_user_id,
+ // true);
+ }
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
@@ -993,19 +1005,31 @@ void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ Core::HID::NpadStyleSet supported_style_set{};
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result =
+ npad->GetSupportedNpadStyleSet(applet_resource_user_id, supported_style_set);
+
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw);
+ rb.Push(result);
+ rb.PushEnum(supported_style_set);
}
void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
-
- const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer());
+ const auto buffer = ctx.ReadBuffer();
+ const std::size_t elements = ctx.GetReadBufferNumElements<Core::HID::NpadIdType>();
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ std::vector<Core::HID::NpadIdType> supported_npad_list(elements);
+ memcpy(supported_npad_list.data(), buffer.data(), buffer.size());
+
+ const auto npad = GetResourceManager()->GetNpad();
+ const Result result =
+ npad->SetSupportedNpadIdType(applet_resource_user_id, supported_npad_list);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
@@ -1018,7 +1042,7 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) {
auto npad = GetResourceManager()->GetNpad();
- // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
+ npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
const Result result = npad->Activate(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -1052,13 +1076,13 @@ void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
- // Games expect this event to be signaled after calling this function
- GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id);
+ Kernel::KReadableEvent* style_set_update_event;
+ const auto result = GetResourceManager()->GetNpad()->AcquireNpadStyleSetUpdateEventHandle(
+ parameters.applet_resource_user_id, &style_set_update_event, parameters.npad_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(
- GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
+ rb.Push(result);
+ rb.PushCopyObjects(style_set_update_event);
}
void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
@@ -1073,7 +1097,7 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
auto controller = GetResourceManager()->GetNpad();
- controller->DisconnectNpad(parameters.npad_id);
+ controller->DisconnectNpad(parameters.applet_resource_user_id, parameters.npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -1113,7 +1137,7 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
auto npad = GetResourceManager()->GetNpad();
- // TODO: npad->SetRevision(applet_resource_user_id, revision);
+ npad->SetRevision(parameters.applet_resource_user_id, parameters.revision);
const auto result = npad->Activate(parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -1125,13 +1149,19 @@ void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto hold_type{rp.PopEnum<NpadJoyHoldType>()};
- GetResourceManager()->GetNpad()->SetHoldType(hold_type);
-
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
applet_resource_user_id, hold_type);
+ if (hold_type != NpadJoyHoldType::Horizontal && hold_type != NpadJoyHoldType::Vertical) {
+ // This should crash console
+ ASSERT_MSG(false, "Invalid npad joy hold type");
+ }
+
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result = npad->SetNpadJoyHoldType(applet_resource_user_id, hold_type);
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
@@ -1140,9 +1170,13 @@ void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ NpadJoyHoldType hold_type{};
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result = npad->GetNpadJoyHoldType(applet_resource_user_id, hold_type);
+
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType());
+ rb.Push(result);
+ rb.PushEnum(hold_type);
}
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
@@ -1158,8 +1192,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx)
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
- controller->SetNpadMode(new_npad_id, parameters.npad_id, NpadJoyDeviceType::Left,
- NpadJoyAssignmentMode::Single);
+ controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
+ NpadJoyDeviceType::Left, NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -1182,8 +1216,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
- controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
- NpadJoyAssignmentMode::Single);
+ controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
+ parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
@@ -1206,7 +1240,8 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
- controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NpadJoyAssignmentMode::Dual);
+ controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, {},
+ NpadJoyAssignmentMode::Dual);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id); // Spams a lot when controller applet is open
@@ -1222,7 +1257,8 @@ void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
const auto applet_resource_user_id{rp.Pop<u64>()};
auto controller = GetResourceManager()->GetNpad();
- const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
+ const auto result =
+ controller->MergeSingleJoyAsDualJoy(applet_resource_user_id, npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
@@ -1235,10 +1271,10 @@ void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- GetResourceManager()->GetNpad()->StartLRAssignmentMode();
-
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ GetResourceManager()->GetNpad()->StartLrAssignmentMode(applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -1247,10 +1283,10 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- GetResourceManager()->GetNpad()->StopLRAssignmentMode();
-
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ GetResourceManager()->GetNpad()->StopLrAssignmentMode(applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -1260,13 +1296,23 @@ void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()};
- GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
-
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
applet_resource_user_id, activation_mode);
+ if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
+ // Console should crash here
+ ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result =
+ npad->SetNpadHandheldActivationMode(applet_resource_user_id, activation_mode);
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
@@ -1275,9 +1321,14 @@ void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ NpadHandheldActivationMode activation_mode{};
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result =
+ npad->GetNpadHandheldActivationMode(applet_resource_user_id, activation_mode);
+
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode());
+ rb.Push(result);
+ rb.PushEnum(activation_mode);
}
void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
@@ -1286,12 +1337,12 @@ void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- auto controller = GetResourceManager()->GetNpad();
- const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
-
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result = npad->SwapNpadAssignment(applet_resource_user_id, npad_id_1, npad_id_2);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
@@ -1307,13 +1358,19 @@ void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext&
const auto parameters{rp.PopRaw<Parameters>()};
- bool is_enabled = false;
- auto controller = GetResourceManager()->GetNpad();
- const auto result =
- controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ if (!IsNpadIdValid(parameters.npad_id)) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultInvalidNpadId);
+ return;
+ }
+
+ bool is_enabled{};
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result = npad->IsUnintendedHomeButtonInputProtectionEnabled(
+ is_enabled, parameters.applet_resource_user_id, parameters.npad_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
@@ -1332,13 +1389,18 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct
const auto parameters{rp.PopRaw<Parameters>()};
- auto controller = GetResourceManager()->GetNpad();
- const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled(
- parameters.is_enabled, parameters.npad_id);
+ LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
+ parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
- LOG_DEBUG(Service_HID,
- "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
- parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
+ if (!IsNpadIdValid(parameters.npad_id)) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultInvalidNpadId);
+ return;
+ }
+
+ const auto npad = GetResourceManager()->GetNpad();
+ const auto result = npad->EnableUnintendedHomeButtonInputProtection(
+ parameters.applet_resource_user_id, parameters.npad_id, parameters.is_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -1359,8 +1421,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
const auto is_reassigned =
- controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
- NpadJoyAssignmentMode::Single);
+ controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
+ parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
@@ -1375,7 +1437,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- bool analog_stick_use_center_clamp;
+ bool use_center_clamp;
INSERT_PADDING_BYTES_NOINIT(7);
u64 applet_resource_user_id;
};
@@ -1383,12 +1445,11 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp(
- parameters.analog_stick_use_center_clamp);
+ LOG_WARNING(Service_HID, "(STUBBED) called, use_center_clamp={}, applet_resource_user_id={}",
+ parameters.use_center_clamp, parameters.applet_resource_user_id);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
- parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
+ GetResourceManager()->GetNpad()->SetNpadAnalogStickUseCenterClamp(
+ parameters.applet_resource_user_id, parameters.use_center_clamp);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1496,7 +1557,8 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
+ GetResourceManager()->GetNpad()->VibrateController(parameters.applet_resource_user_id,
+ parameters.vibration_device_handle,
parameters.vibration_value);
LOG_DEBUG(Service_HID,
@@ -1528,8 +1590,8 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
- rb.PushRaw(
- GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle));
+ rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration(
+ parameters.applet_resource_user_id, parameters.vibration_device_handle));
}
void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
@@ -1580,7 +1642,8 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
auto vibration_values = std::span(
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
- GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values);
+ GetResourceManager()->GetNpad()->VibrateControllers(applet_resource_user_id,
+ vibration_device_handles, vibration_values);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
@@ -1634,8 +1697,8 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
}
}();
- GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
- vibration_value);
+ GetResourceManager()->GetNpad()->VibrateController(
+ parameters.applet_resource_user_id, parameters.vibration_device_handle, vibration_value);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
@@ -1659,8 +1722,8 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- const auto last_vibration =
- GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle);
+ const auto last_vibration = GetResourceManager()->GetNpad()->GetLastVibration(
+ parameters.applet_resource_user_id, parameters.vibration_device_handle);
const auto gc_erm_command = [last_vibration] {
if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
@@ -1732,7 +1795,7 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
- parameters.vibration_device_handle));
+ parameters.applet_resource_user_id, parameters.vibration_device_handle));
}
void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
@@ -1850,8 +1913,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
- auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_1_handle);
+ auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
if (t_mem_1.IsNull()) {
LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
@@ -1860,8 +1922,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
return;
}
- auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_2_handle);
+ auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
if (t_mem_2.IsNull()) {
LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
@@ -2142,8 +2203,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
- auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_handle);
+ auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -2318,10 +2378,10 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()};
- GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, communication_mode={}",
+ applet_resource_user_id, communication_mode);
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
- applet_resource_user_id, communication_mode);
+ // This function has been stubbed since 2.0.0+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -2329,12 +2389,15 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ // This function has been stubbed since 2.0.0+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode());
+ rb.PushEnum(NpadCommunicationMode::Default);
}
void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 5cc88c4a1..4823de743 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -240,9 +240,12 @@ IHidSystemServer::~IHidSystemServer() {
};
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
+ GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -271,9 +274,12 @@ void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
}
void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
+ LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -298,28 +304,32 @@ void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_INFO(Service_HID, "(STUBBED) called");
+ LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- Core::HID::NpadStyleSet supported_styleset =
- GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
+ Core::HID::NpadStyleSet supported_styleset{};
+ const auto& npad = GetResourceManager()->GetNpad();
+ const Result result =
+ npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
+ rb.Push(result);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_INFO(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- Core::HID::NpadStyleSet supported_styleset =
- GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw;
+ const auto& npad = GetResourceManager()->GetNpad();
+ const auto result =
+ npad->SetSupportedNpadStyleSet(applet_resource_user_id, Core::HID::NpadStyleSet::All);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(supported_styleset);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
}
void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h
index b87cc10e3..6a2ed287a 100644
--- a/src/core/hle/service/hid/hid_util.h
+++ b/src/core/hle/service/hid/hid_util.h
@@ -31,7 +31,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand
const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
if (!npad_id) {
- return InvalidNpadId;
+ return ResultInvalidNpadId;
}
if (!device_index) {
return NpadDeviceIndexOutOfRange;
@@ -54,15 +54,15 @@ constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle&
// These support vibration
break;
default:
- return VibrationInvalidStyleIndex;
+ return ResultVibrationInvalidStyleIndex;
}
if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
- return VibrationInvalidNpadId;
+ return ResultVibrationInvalidNpadId;
}
if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
- return VibrationDeviceIndexOutOfRange;
+ return ResultVibrationDeviceIndexOutOfRange;
}
return ResultSuccess;
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 80aac221b..ffa7e144d 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
// Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback",
- [this](std::uintptr_t user_data, s64 time,
+ [this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
- UpdateHidbus(user_data, ns_late);
+ UpdateHidbus(ns_late);
return std::nullopt;
});
@@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
}
HidBus::~HidBus() {
- system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
+ system.CoreTiming().UnscheduleEvent(hidbus_update_event);
}
-void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initializated) {
@@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
- auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_handle);
+ auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index c29b5e882..85a1df133 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -108,7 +108,7 @@ private:
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
void SetStatusManagerType(HLERequestContext& ctx);
- void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void UpdateHidbus(std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
template <typename T>
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 39b9a4474..05ed31273 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
- auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_handle);
+ auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -316,7 +315,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(Service::HID::InvalidNpadId);
+ rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
@@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
- auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
- t_mem_handle);
+ auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index 6c6cbd802..1f41e645d 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -10,18 +10,23 @@
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/hid/controllers/applet_resource.h"
+#include "core/hle/service/hid/controllers/capture_button.h"
#include "core/hle/service/hid/controllers/console_six_axis.h"
+#include "core/hle/service/hid/controllers/debug_mouse.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
+#include "core/hle/service/hid/controllers/digitizer.h"
#include "core/hle/service/hid/controllers/gesture.h"
+#include "core/hle/service/hid/controllers/home_button.h"
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/hle/service/hid/controllers/mouse.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/hid/controllers/seven_six_axis.h"
-#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/six_axis.h"
-#include "core/hle/service/hid/controllers/stubbed.h"
+#include "core/hle/service/hid/controllers/sleep_button.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
+#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
+#include "core/hle/service/hid/controllers/unique_pad.h"
namespace Service::HID {
@@ -46,42 +51,13 @@ void ResourceManager::Initialize() {
}
system.HIDCore().ReloadInputDevices();
- is_initialized = true;
-}
-
-void ResourceManager::InitializeController(u64 aruid) {
- SharedMemoryFormat* shared_memory = nullptr;
- const auto result = applet_resource->GetSharedMemoryFormat(&shared_memory, aruid);
- if (result.IsError()) {
- return;
- }
- debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory->debug_pad);
- mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory->mouse);
- debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory->debug_mouse);
- keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory->keyboard);
- unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory->unique_pad.header);
- npad = std::make_shared<NPad>(system.HIDCore(), shared_memory->npad, service_context);
- gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory->gesture);
- touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory->touch_screen);
+ InitializeHidCommonSampler();
+ InitializeTouchScreenSampler();
+ InitializeConsoleSixAxisSampler();
+ InitializeAHidSampler();
- palma = std::make_shared<Palma>(system.HIDCore(), service_context);
-
- home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory->home_button.header);
- sleep_button =
- std::make_shared<SleepButton>(system.HIDCore(), shared_memory->sleep_button.header);
- capture_button =
- std::make_shared<CaptureButton>(system.HIDCore(), shared_memory->capture_button.header);
- digitizer = std::make_shared<Digitizer>(system.HIDCore(), shared_memory->digitizer.header);
-
- six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
- console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory->console);
- seven_six_axis = std::make_shared<SevenSixAxis>(system);
-
- // Homebrew doesn't try to activate some controllers, so we activate them by default
- npad->Activate();
- six_axis->Activate();
- touch_screen->Activate();
+ is_initialized = true;
}
std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
@@ -153,28 +129,77 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
}
Result ResourceManager::CreateAppletResource(u64 aruid) {
- if (aruid == 0) {
+ if (aruid == SystemAruid) {
const auto result = RegisterCoreAppletResource();
if (result.IsError()) {
return result;
}
- return GetNpad()->Activate();
+ return GetNpad()->ActivateNpadResource();
}
const auto result = CreateAppletResourceImpl(aruid);
if (result.IsError()) {
return result;
}
- return GetNpad()->Activate(aruid);
+
+ // Homebrew doesn't try to activate some controllers, so we activate them by default
+ npad->Activate();
+ six_axis->Activate();
+ touch_screen->Activate();
+
+ return GetNpad()->ActivateNpadResource(aruid);
}
Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
std::scoped_lock lock{shared_mutex};
- const auto result = applet_resource->CreateAppletResource(aruid);
- if (result.IsSuccess()) {
- InitializeController(aruid);
- }
- return result;
+ return applet_resource->CreateAppletResource(aruid);
+}
+
+void ResourceManager::InitializeHidCommonSampler() {
+ debug_pad = std::make_shared<DebugPad>(system.HIDCore());
+ mouse = std::make_shared<Mouse>(system.HIDCore());
+ debug_mouse = std::make_shared<DebugMouse>(system.HIDCore());
+ keyboard = std::make_shared<Keyboard>(system.HIDCore());
+ unique_pad = std::make_shared<UniquePad>(system.HIDCore());
+ npad = std::make_shared<NPad>(system.HIDCore(), service_context);
+ gesture = std::make_shared<Gesture>(system.HIDCore());
+ home_button = std::make_shared<HomeButton>(system.HIDCore());
+ sleep_button = std::make_shared<SleepButton>(system.HIDCore());
+ capture_button = std::make_shared<CaptureButton>(system.HIDCore());
+ digitizer = std::make_shared<Digitizer>(system.HIDCore());
+
+ palma = std::make_shared<Palma>(system.HIDCore(), service_context);
+ six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
+
+ debug_pad->SetAppletResource(applet_resource, &shared_mutex);
+ digitizer->SetAppletResource(applet_resource, &shared_mutex);
+ keyboard->SetAppletResource(applet_resource, &shared_mutex);
+ npad->SetNpadExternals(applet_resource, &shared_mutex);
+ six_axis->SetAppletResource(applet_resource, &shared_mutex);
+ mouse->SetAppletResource(applet_resource, &shared_mutex);
+ debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
+ home_button->SetAppletResource(applet_resource, &shared_mutex);
+ sleep_button->SetAppletResource(applet_resource, &shared_mutex);
+ capture_button->SetAppletResource(applet_resource, &shared_mutex);
+}
+
+void ResourceManager::InitializeTouchScreenSampler() {
+ gesture = std::make_shared<Gesture>(system.HIDCore());
+ touch_screen = std::make_shared<TouchScreen>(system.HIDCore());
+
+ touch_screen->SetAppletResource(applet_resource, &shared_mutex);
+ gesture->SetAppletResource(applet_resource, &shared_mutex);
+}
+
+void ResourceManager::InitializeConsoleSixAxisSampler() {
+ console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore());
+ seven_six_axis = std::make_shared<SevenSixAxis>(system);
+
+ console_six_axis->SetAppletResource(applet_resource, &shared_mutex);
+}
+
+void ResourceManager::InitializeAHidSampler() {
+ // TODO
}
Result ResourceManager::RegisterCoreAppletResource() {
@@ -189,7 +214,11 @@ Result ResourceManager::UnregisterCoreAppletResource() {
Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
std::scoped_lock lock{shared_mutex};
- return applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
+ auto result = applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
+ if (result.IsSuccess()) {
+ result = npad->RegisterAppletResourceUserId(aruid);
+ }
+ return result;
}
void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) {
@@ -227,8 +256,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
applet_resource->EnableTouchScreen(aruid, is_enabled);
}
-void ResourceManager::UpdateControllers(std::uintptr_t user_data,
- std::chrono::nanoseconds ns_late) {
+void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
debug_pad->OnUpdate(core_timing);
digitizer->OnUpdate(core_timing);
@@ -241,20 +269,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
capture_button->OnUpdate(core_timing);
}
-void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
npad->OnUpdate(core_timing);
}
-void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
- std::chrono::nanoseconds ns_late) {
+void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
mouse->OnUpdate(core_timing);
debug_mouse->OnUpdate(core_timing);
keyboard->OnUpdate(core_timing);
}
-void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
six_axis->OnUpdate(core_timing);
seven_six_axis->OnUpdate(core_timing);
@@ -273,34 +300,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
// Register update callbacks
npad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
- [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
- -> std::optional<std::chrono::nanoseconds> {
+ [this, resource](
+ s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
- resource->UpdateNpad(user_data, ns_late);
+ resource->UpdateNpad(ns_late);
return std::nullopt;
});
default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback",
- [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
- -> std::optional<std::chrono::nanoseconds> {
+ [this, resource](
+ s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
- resource->UpdateControllers(user_data, ns_late);
+ resource->UpdateControllers(ns_late);
return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
- [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
- -> std::optional<std::chrono::nanoseconds> {
+ [this, resource](
+ s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
- resource->UpdateMouseKeyboard(user_data, ns_late);
+ resource->UpdateMouseKeyboard(ns_late);
return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
- [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
- -> std::optional<std::chrono::nanoseconds> {
+ [this, resource](
+ s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
- resource->UpdateMotion(user_data, ns_late);
+ resource->UpdateMotion(ns_late);
return std::nullopt;
});
@@ -314,10 +341,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
}
IAppletResource::~IAppletResource() {
- system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
- system.CoreTiming().UnscheduleEvent(default_update_event, 0);
- system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
- system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
+ system.CoreTiming().UnscheduleEvent(npad_update_event);
+ system.CoreTiming().UnscheduleEvent(default_update_event);
+ system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
+ system.CoreTiming().UnscheduleEvent(motion_update_event);
resource_manager->FreeAppletResourceId(aruid);
}
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 5ad7cb564..7a21d8eb8 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -20,24 +20,23 @@ class KSharedMemory;
namespace Service::HID {
class AppletResource;
+class CaptureButton;
class Controller_Stubbed;
class ConsoleSixAxis;
+class DebugMouse;
class DebugPad;
+class Digitizer;
class Gesture;
+class HomeButton;
class Keyboard;
class Mouse;
class NPad;
class Palma;
class SevenSixAxis;
class SixAxis;
+class SleepButton;
class TouchScreen;
-
-using CaptureButton = Controller_Stubbed;
-using DebugMouse = Mouse;
-using Digitizer = Controller_Stubbed;
-using HomeButton = Controller_Stubbed;
-using SleepButton = Controller_Stubbed;
-using UniquePad = Controller_Stubbed;
+class UniquePad;
class ResourceManager {
@@ -46,7 +45,6 @@ public:
~ResourceManager();
void Initialize();
- void InitializeController(u64 aruid);
std::shared_ptr<AppletResource> GetAppletResource() const;
std::shared_ptr<CaptureButton> GetCaptureButton() const;
@@ -81,17 +79,21 @@ public:
void EnablePadInput(u64 aruid, bool is_enabled);
void EnableTouchScreen(u64 aruid, bool is_enabled);
- void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
- void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
- void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
- void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void UpdateControllers(std::chrono::nanoseconds ns_late);
+ void UpdateNpad(std::chrono::nanoseconds ns_late);
+ void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
+ void UpdateMotion(std::chrono::nanoseconds ns_late);
private:
Result CreateAppletResourceImpl(u64 aruid);
+ void InitializeHidCommonSampler();
+ void InitializeTouchScreenSampler();
+ void InitializeConsoleSixAxisSampler();
+ void InitializeAHidSampler();
bool is_initialized{false};
- mutable std::mutex shared_mutex;
+ mutable std::recursive_mutex shared_mutex;
std::shared_ptr<AppletResource> applet_resource = nullptr;
std::shared_ptr<CaptureButton> capture_button = nullptr;
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 38955932c..39df77e43 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
HLERequestContext::~HLERequestContext() = default;
-void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
- bool incoming) {
- client_handle_table = &process.GetHandleTable();
-
+void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = rp.PopRaw<IPC::CommandHeader>();
@@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
if (command_header->enable_handle_descriptor) {
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
if (handle_descriptor_header->send_current_pid) {
- pid = process.GetProcessId();
+ pid = thread->GetOwnerProcess()->GetProcessId();
rp.Skip(2, false);
}
if (incoming) {
@@ -270,9 +267,10 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
-Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
- u32_le* src_cmdbuf) {
- ParseCommandBuffer(process, src_cmdbuf, true);
+Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
+ client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
+
+ ParseCommandBuffer(src_cmdbuf, true);
if (command_header->IsCloseCommand()) {
// Close does not populate the rest of the IPC header
@@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr
return ResultSuccess;
}
-Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) {
+Result HLERequestContext::WriteToOutgoingCommandBuffer() {
auto current_offset = handles_offset;
- auto& owner_process = *requesting_thread.GetOwnerProcess();
+ auto& owner_process = *thread->GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
for (auto& object : outgoing_copy_objects) {
@@ -319,7 +317,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesti
}
// Copy the translated command buffer back into the thread's command buffer area.
- memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
+ memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
return ResultSuccess;
}
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 18d464c63..40d86943e 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -17,6 +17,7 @@
#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
+#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/svc_common.h"
union Result;
@@ -196,10 +197,10 @@ public:
}
/// Populates this context with data from the requesting process/thread.
- Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf);
+ Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
- Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
+ Result WriteToOutgoingCommandBuffer();
[[nodiscard]] u32_le GetHipcCommand() const {
return command;
@@ -359,8 +360,17 @@ public:
return *thread;
}
- Kernel::KHandleTable& GetClientHandleTable() {
- return *client_handle_table;
+ [[nodiscard]] Core::Memory::Memory& GetMemory() const {
+ return memory;
+ }
+
+ template <typename T>
+ Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
+ auto obj = client_handle_table->GetObjectForIpc(handle, thread);
+ if (obj.IsNotNull()) {
+ return obj->DynamicCast<T*>();
+ }
+ return nullptr;
}
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
@@ -378,7 +388,7 @@ public:
private:
friend class IPC::ResponseBuilder;
- void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming);
+ void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
Kernel::KServerSession* server_session{};
diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h
index 0e222362e..4b02872fb 100644
--- a/src/core/hle/service/ipc_helpers.h
+++ b/src/core/hle/service/ipc_helpers.h
@@ -151,8 +151,8 @@ public:
if (manager->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
- kernel.ApplicationProcess()->GetResourceLimit()->Reserve(
- Kernel::LimitableResource::SessionCountMax, 1);
+ ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
+ Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 65851fc05..77aa6d7d1 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -4,11 +4,11 @@
#include "core/arm/debug.h"
#include "core/arm/symbols.h"
#include "core/core.h"
-#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/jit/jit.h"
+#include "core/hle/service/jit/jit_code_memory.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
@@ -23,9 +23,11 @@ struct CodeRange {
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
public:
- explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx,
- CodeRange user_ro)
- : ServiceFramework{system_, "IJitEnvironment"}, process{&process_},
+ explicit IJitEnvironment(Core::System& system_,
+ Kernel::KScopedAutoObject<Kernel::KProcess>&& process_,
+ CodeMemory&& user_rx_, CodeMemory&& user_ro_)
+ : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
+ user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
context{system_.ApplicationMemory()} {
// clang-format off
static const FunctionInfo functions[] = {
@@ -39,10 +41,13 @@ public:
RegisterHandlers(functions);
// Identity map user code range into sysmodule context
- configuration.user_ro_memory = user_ro;
- configuration.user_rx_memory = user_rx;
- configuration.sys_ro_memory = user_ro;
- configuration.sys_rx_memory = user_rx;
+ configuration.user_rx_memory.size = user_rx.GetSize();
+ configuration.user_rx_memory.offset = user_rx.GetAddress();
+ configuration.user_ro_memory.size = user_ro.GetSize();
+ configuration.user_ro_memory.offset = user_ro.GetAddress();
+
+ configuration.sys_rx_memory = configuration.user_rx_memory;
+ configuration.sys_ro_memory = configuration.user_ro_memory;
}
void GenerateCode(HLERequestContext& ctx) {
@@ -188,7 +193,7 @@ public:
return;
}
- auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};
+ auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
if (tmem.IsNull()) {
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
IPC::ResponseBuilder rb{ctx, 2};
@@ -318,6 +323,8 @@ private:
}
Kernel::KScopedAutoObject<Kernel::KProcess> process;
+ CodeMemory user_rx;
+ CodeMemory user_ro;
GuestCallbacks callbacks;
JITConfiguration configuration;
JITContext context;
@@ -335,6 +342,7 @@ public:
RegisterHandlers(functions);
}
+private:
void CreateJitEnvironment(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
@@ -356,11 +364,7 @@ public:
return;
}
- // Fetch using the handle table for the application process here,
- // since we are not multiprocess yet.
- const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
-
- auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
if (process.IsNull()) {
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
IPC::ResponseBuilder rb{ctx, 2};
@@ -368,7 +372,7 @@ public:
return;
}
- auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)};
+ auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
@@ -376,7 +380,7 @@ public:
return;
}
- auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)};
+ auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
if (ro_mem.IsNull()) {
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
@@ -384,20 +388,35 @@ public:
return;
}
- const CodeRange user_rx{
- .offset = GetInteger(rx_mem->GetSourceAddress()),
- .size = parameters.rx_size,
- };
+ CodeMemory rx, ro;
+ Result res;
- const CodeRange user_ro{
- .offset = GetInteger(ro_mem->GetSourceAddress()),
- .size = parameters.ro_size,
- };
+ res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
+ Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
+ if (R_FAILED(res)) {
+ LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res);
+ return;
+ }
+
+ res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
+ Kernel::Svc::MemoryPermission::Read, generate_random);
+ if (R_FAILED(res)) {
+ LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro);
+ rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
+ std::move(ro));
}
+
+private:
+ std::mt19937_64 generate_random{};
};
void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/jit/jit_code_memory.cpp b/src/core/hle/service/jit/jit_code_memory.cpp
new file mode 100644
index 000000000..2b480488a
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.cpp
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/jit/jit_code_memory.h"
+
+namespace Service::JIT {
+
+Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory,
+ size_t size, Kernel::Svc::MemoryPermission perm,
+ std::mt19937_64& generate_random) {
+ auto& page_table = process.GetPageTable();
+ const u64 alias_code_start =
+ GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize;
+ const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize;
+
+ // NOTE: This will retry indefinitely until mapping the code memory succeeds.
+ while (true) {
+ // Generate a new trial address.
+ const u64 mapped_address =
+ (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize;
+
+ // Try to map the address
+ R_TRY_CATCH(code_memory.MapToOwner(mapped_address, size, perm)) {
+ R_CATCH(Kernel::ResultInvalidMemoryRegion) {
+ // If we could not map here, retry.
+ continue;
+ }
+ }
+ R_END_TRY_CATCH;
+
+ // Set members.
+ m_code_memory = std::addressof(code_memory);
+ m_size = size;
+ m_address = mapped_address;
+ m_perm = perm;
+
+ // Open a new reference to the code memory.
+ m_code_memory->Open();
+
+ // We succeeded.
+ R_SUCCEED();
+ }
+}
+
+void CodeMemory::Finalize() {
+ if (m_code_memory) {
+ R_ASSERT(m_code_memory->UnmapFromOwner(m_address, m_size));
+ m_code_memory->Close();
+ }
+
+ m_code_memory = nullptr;
+}
+
+} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_code_memory.h b/src/core/hle/service/jit/jit_code_memory.h
new file mode 100644
index 000000000..6376d4c4e
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <random>
+
+#include "core/hle/kernel/k_code_memory.h"
+
+namespace Service::JIT {
+
+class CodeMemory {
+public:
+ YUZU_NON_COPYABLE(CodeMemory);
+
+ explicit CodeMemory() = default;
+
+ CodeMemory(CodeMemory&& rhs) {
+ std::swap(m_code_memory, rhs.m_code_memory);
+ std::swap(m_size, rhs.m_size);
+ std::swap(m_address, rhs.m_address);
+ std::swap(m_perm, rhs.m_perm);
+ }
+
+ ~CodeMemory() {
+ this->Finalize();
+ }
+
+public:
+ Result Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size,
+ Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random);
+ void Finalize();
+
+ size_t GetSize() const {
+ return m_size;
+ }
+
+ u64 GetAddress() const {
+ return m_address;
+ }
+
+private:
+ Kernel::KCodeMemory* m_code_memory{};
+ size_t m_size{};
+ u64 m_address{};
+ Kernel::Svc::MemoryPermission m_perm{};
+};
+
+} // namespace Service::JIT
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 6352b09a9..aa8aaa2d9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
// Schedule the screen composition events
multi_composition_event = Core::Timing::CreateEvent(
"ScreenComposition",
- [this](std::uintptr_t, s64 time,
+ [this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
vsync_signal.Set();
return std::chrono::nanoseconds(GetNextTicks());
@@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
single_composition_event = Core::Timing::CreateEvent(
"ScreenComposition",
- [this](std::uintptr_t, s64 time,
+ [this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto lock_guard = Lock();
Compose();
@@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
Nvnflinger::~Nvnflinger() {
if (system.IsMulticore()) {
- system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
+ system.CoreTiming().UnscheduleEvent(multi_composition_event);
vsync_thread.request_stop();
vsync_signal.Set();
} else {
- system.CoreTiming().UnscheduleEvent(single_composition_event, {});
+ system.CoreTiming().UnscheduleEvent(single_composition_event);
}
ShutdownLayers();
diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp
index 17110d3f1..f0658bb5d 100644
--- a/src/core/hle/service/ro/ro.cpp
+++ b/src/core/hle/service/ro/ro.cpp
@@ -651,10 +651,9 @@ private:
void RegisterProcessHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
- auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
+ auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
- auto result = interface.RegisterProcessHandle(client_pid,
- process_h->DynamicCast<Kernel::KProcess*>());
+ auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -671,12 +670,11 @@ private:
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
- auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
+ auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
- auto result =
- interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size,
- process_h->DynamicCast<Kernel::KProcess*>());
+ auto result = interface.RegisterProcessModuleInfo(
+ client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 6808247a9..15edb23e0 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -47,7 +47,7 @@ ServerManager::~ServerManager() {
m_stopped.Wait();
m_threads.clear();
- // Clean up ports.
+ // Clean up server ports.
for (const auto& [port, handler] : m_ports) {
port->Close();
}
@@ -97,22 +97,15 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
u32 max_sessions) {
ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
- // Add the new server to sm:.
- ASSERT(R_SUCCEEDED(
- m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory)));
-
- // Get the registered port.
- Kernel::KPort* port{};
- ASSERT(
- R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
-
- // Open a new reference to the server port.
- port->GetServerPort().Open();
+ // Add the new server to sm: and get the moved server port.
+ Kernel::KServerPort* server_port{};
+ R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
+ max_sessions, handler_factory));
// Begin tracking the server port.
{
std::scoped_lock ll{m_list_mutex};
- m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
+ m_ports.emplace(server_port, std::move(handler_factory));
}
// Signal the wakeup event.
@@ -372,7 +365,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
// Try to receive a message.
std::shared_ptr<HLERequestContext> context;
- rc = session->ReceiveRequest(&context, manager);
+ rc = session->ReceiveRequestHLE(&context, manager);
// If the session has been closed, we're done.
if (rc == Kernel::ResultSessionClosed) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 00531b021..39124c5fd 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -203,7 +203,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
// If emulation was shutdown, we are closing service threads, do not write the response back to
// memory that may be shutting down as well.
if (system.IsPoweredOn()) {
- ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
+ ctx.WriteToOutgoingCommandBuffer();
}
return result;
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 0653779d5..8e637f963 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -507,6 +507,14 @@ void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(0);
+}
+
void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
@@ -926,7 +934,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"},
{59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
{60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
{61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
- {62, nullptr, "GetDebugModeFlag"},
+ {62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"},
{63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
{64, nullptr, "SetPrimaryAlbumStorage"},
{65, nullptr, "GetUsb30EnableFlag"},
@@ -1143,6 +1151,8 @@ void SET_SYS::StoreSettings() {
}
void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
+ Common::SetCurrentThreadName("SettingsStore");
+
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
std::scoped_lock l{m_save_needed_mutex};
if (!std::exchange(m_save_needed, false)) {
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index 3785d93d8..853f76fce 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -98,6 +98,7 @@ private:
void GetSettingsItemValue(HLERequestContext& ctx);
void GetTvSettings(HLERequestContext& ctx);
void SetTvSettings(HLERequestContext& ctx);
+ void GetDebugModeFlag(HLERequestContext& ctx);
void GetQuestFlag(HLERequestContext& ctx);
void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 296ee6e89..1095dcf6c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -29,8 +29,7 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {
ServiceManager::~ServiceManager() {
for (auto& [name, port] : service_ports) {
- port->GetClientPort().Close();
- port->GetServerPort().Close();
+ port->Close();
}
if (deferral_event) {
@@ -50,8 +49,8 @@ static Result ValidateServiceName(const std::string& name) {
return ResultSuccess;
}
-Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
- SessionRequestHandlerFactory handler) {
+Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
+ u32 max_sessions, SessionRequestHandlerFactory handler) {
R_TRY(ValidateServiceName(name));
std::scoped_lock lk{lock};
@@ -66,13 +65,17 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
// Register the port.
Kernel::KPort::Register(kernel, port);
- service_ports.emplace(name, port);
+ service_ports.emplace(name, std::addressof(port->GetClientPort()));
registered_services.emplace(name, handler);
if (deferral_event) {
deferral_event->Signal();
}
- return ResultSuccess;
+ // Set our output.
+ *out_server_port = std::addressof(port->GetServerPort());
+
+ // We succeeded.
+ R_SUCCEED();
}
Result ServiceManager::UnregisterService(const std::string& name) {
@@ -91,7 +94,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
return ResultSuccess;
}
-Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) {
+Result ServiceManager::GetServicePort(Kernel::KClientPort** out_client_port,
+ const std::string& name) {
R_TRY(ValidateServiceName(name));
std::scoped_lock lk{lock};
@@ -101,7 +105,7 @@ Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::strin
return Service::SM::ResultNotRegistered;
}
- *out_port = it->second;
+ *out_client_port = it->second;
return ResultSuccess;
}
@@ -172,8 +176,8 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
std::string name(PopServiceName(rp));
// Find the named port.
- Kernel::KPort* port{};
- auto port_result = service_manager.GetServicePort(&port, name);
+ Kernel::KClientPort* client_port{};
+ auto port_result = service_manager.GetServicePort(&client_port, name);
if (port_result == Service::SM::ResultInvalidServiceName) {
LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
return Service::SM::ResultInvalidServiceName;
@@ -187,7 +191,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
// Create a new session.
Kernel::KClientSession* session{};
- if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
+ if (const auto result = client_port->CreateSession(&session); result.IsError()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
return result;
}
@@ -221,7 +225,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
max_session_count, is_light);
- if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr);
+ Kernel::KServerPort* server_port{};
+ if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
+ max_session_count, nullptr);
result.IsError()) {
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2};
@@ -229,13 +235,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
return;
}
- auto* port = Kernel::KPort::Create(kernel);
- port->Initialize(ServerSessionCountMax, is_light, 0);
- SCOPE_EXIT({ port->GetClientPort().Close(); });
-
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(ResultSuccess);
- rb.PushMoveObjects(port->GetServerPort());
+ rb.PushMoveObjects(server_port);
}
void SM::UnregisterService(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index ff74f588a..4ae32a9c1 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -56,10 +56,10 @@ public:
explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager();
- Result RegisterService(std::string name, u32 max_sessions,
- SessionRequestHandlerFactory handler_factory);
+ Result RegisterService(Kernel::KServerPort** out_server_port, std::string name,
+ u32 max_sessions, SessionRequestHandlerFactory handler_factory);
Result UnregisterService(const std::string& name);
- Result GetServicePort(Kernel::KPort** out_port, const std::string& name);
+ Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
template <Common::DerivedFrom<SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -84,7 +84,7 @@ private:
/// Map of registered services, retrieved using GetServicePort.
std::mutex lock;
std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
- std::unordered_map<std::string, Kernel::KPort*> service_ports;
+ std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
/// Kernel context
Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 7dce28fe0..7f0fb91d0 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -28,7 +28,6 @@ void Controller::ConvertCurrentObjectToDomain(HLERequestContext& ctx) {
void Controller::CloneCurrentObject(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called");
- auto& process = *ctx.GetThread().GetOwnerProcess();
auto session_manager = ctx.GetManager();
// FIXME: this is duplicated from the SVC, it should just call it instead
@@ -36,11 +35,11 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
// Reserve a new session from the process resource limit.
Kernel::KScopedResourceReservation session_reservation(
- &process, Kernel::LimitableResource::SessionCountMax);
+ Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax);
ASSERT(session_reservation.Succeeded());
// Create the session.
- Kernel::KSession* session = Kernel::KSession::Create(system.Kernel());
+ Kernel::KSession* session = Kernel::KSession::Create(kernel);
ASSERT(session != nullptr);
// Initialize the session.
@@ -50,7 +49,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
session_reservation.Commit();
// Register the session.
- Kernel::KSession::Register(system.Kernel(), session);
+ Kernel::KSession::Register(kernel, session);
// Register with server manager.
session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 60ee78e89..c9f8707b7 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -129,9 +129,10 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
metadata.Print();
- // Enable NCE only for programs with 39-bit address space.
+ // Enable NCE only for applications with 39-bit address space.
const bool is_39bit =
metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
+ const bool is_application = metadata.GetPoolPartition() == FileSys::PoolPartition::Application;
Settings::SetNceEnabled(is_39bit);
const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
@@ -147,7 +148,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
#ifdef HAS_NCE
- if (Settings::IsNceEnabled()) {
+ if (is_application && Settings::IsNceEnabled()) {
return &module_patchers[i];
}
#endif
@@ -175,7 +176,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// Enable direct memory mapping in case of NCE.
const u64 fastmem_base = [&]() -> size_t {
- if (Settings::IsNceEnabled()) {
+ if (is_application && Settings::IsNceEnabled()) {
auto& buffer = system.DeviceMemory().buffer;
buffer.EnableDirectMappedAddress();
return reinterpret_cast<u64>(buffer.VirtualBasePointer());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 169bf4c8c..8176a41be 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/atomic_ops.h"
#include "common/common_types.h"
+#include "common/heap_tracker.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/scope_exit.h"
@@ -45,11 +46,25 @@ struct Memory::Impl {
void SetCurrentPageTable(Kernel::KProcess& process) {
current_page_table = &process.GetPageTable().GetImpl();
- current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
+
+ if (std::addressof(process) == system.ApplicationProcess() &&
+ Settings::IsFastmemEnabled()) {
+ current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
+ } else {
+ current_page_table->fastmem_arena = nullptr;
+ }
+
+#ifdef __linux__
+ heap_tracker.emplace(system.DeviceMemory().buffer);
+ buffer = std::addressof(*heap_tracker);
+#else
+ buffer = std::addressof(system.DeviceMemory().buffer);
+#endif
}
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
- Common::PhysicalAddress target, Common::MemoryPermission perms) {
+ Common::PhysicalAddress target, Common::MemoryPermission perms,
+ bool separate_heap) {
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
@@ -57,20 +72,21 @@ struct Memory::Impl {
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
Common::PageType::Memory);
- if (Settings::IsFastmemEnabled()) {
- system.DeviceMemory().buffer.Map(GetInteger(base),
- GetInteger(target) - DramMemoryMap::Base, size, perms);
+ if (current_page_table->fastmem_arena) {
+ buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
+ separate_heap);
}
}
- void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) {
+ void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
+ bool separate_heap) {
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
Common::PageType::Unmapped);
- if (Settings::IsFastmemEnabled()) {
- system.DeviceMemory().buffer.Unmap(GetInteger(base), size);
+ if (current_page_table->fastmem_arena) {
+ buffer->Unmap(GetInteger(base), size, separate_heap);
}
}
@@ -79,17 +95,7 @@ struct Memory::Impl {
ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
- if (!Settings::IsFastmemEnabled()) {
- return;
- }
-
- const bool is_r = True(perms & Common::MemoryPermission::Read);
- const bool is_w = True(perms & Common::MemoryPermission::Write);
- const bool is_x =
- True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
-
- if (!current_page_table) {
- system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
+ if (!current_page_table->fastmem_arena) {
return;
}
@@ -101,8 +107,7 @@ struct Memory::Impl {
switch (page_type) {
case Common::PageType::RasterizerCachedMemory:
if (protect_bytes > 0) {
- system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w,
- is_x);
+ buffer->Protect(protect_begin, protect_bytes, perms);
protect_bytes = 0;
}
break;
@@ -115,7 +120,7 @@ struct Memory::Impl {
}
if (protect_bytes > 0) {
- system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x);
+ buffer->Protect(protect_begin, protect_bytes, perms);
}
}
@@ -239,7 +244,7 @@ struct Memory::Impl {
bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
auto on_memory, auto on_rasterizer, auto increment) {
- const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl();
+ const auto& page_table = *current_page_table;
std::size_t remaining_size = size;
std::size_t page_index = addr >> YUZU_PAGEBITS;
std::size_t page_offset = addr & YUZU_PAGEMASK;
@@ -484,8 +489,10 @@ struct Memory::Impl {
return;
}
- if (Settings::IsFastmemEnabled()) {
- system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
+ if (current_page_table->fastmem_arena) {
+ const auto perm{debug ? Common::MemoryPermission{}
+ : Common::MemoryPermission::ReadWrite};
+ buffer->Protect(vaddr, size, perm);
}
// Iterate over a contiguous CPU address space, marking/unmarking the region.
@@ -541,10 +548,15 @@ struct Memory::Impl {
return;
}
- if (Settings::IsFastmemEnabled()) {
- const bool is_read_enable =
- !Settings::values.use_reactive_flushing.GetValue() || !cached;
- system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
+ if (current_page_table->fastmem_arena) {
+ Common::MemoryPermission perm{};
+ if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
+ perm |= Common::MemoryPermission::Read;
+ }
+ if (!cached) {
+ perm |= Common::MemoryPermission::Write;
+ }
+ buffer->Protect(vaddr, size, perm);
}
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
@@ -855,6 +867,13 @@ struct Memory::Impl {
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
std::mutex sys_core_guard;
+
+ std::optional<Common::HeapTracker> heap_tracker;
+#ifdef __linux__
+ Common::HeapTracker* buffer{};
+#else
+ Common::HostMemory* buffer{};
+#endif
};
Memory::Memory(Core::System& system_) : system{system_} {
@@ -872,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) {
}
void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
- Common::PhysicalAddress target, Common::MemoryPermission perms) {
- impl->MapMemoryRegion(page_table, base, size, target, perms);
+ Common::PhysicalAddress target, Common::MemoryPermission perms,
+ bool separate_heap) {
+ impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap);
}
-void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) {
- impl->UnmapRegion(page_table, base, size);
+void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
+ bool separate_heap) {
+ impl->UnmapRegion(page_table, base, size, separate_heap);
}
void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
@@ -886,8 +907,7 @@ void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress
}
bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
- const Kernel::KProcess& process = *system.ApplicationProcess();
- const auto& page_table = process.GetPageTable().GetImpl();
+ const auto& page_table = *impl->current_page_table;
const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) {
return false;
@@ -1048,7 +1068,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
}
bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
- bool mapped = true;
+ [[maybe_unused]] bool mapped = true;
+ [[maybe_unused]] bool rasterizer = false;
+
u8* const ptr = impl->GetPointerImpl(
GetInteger(vaddr),
[&] {
@@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
GetInteger(vaddr));
mapped = false;
},
- [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); });
+ [&] {
+ impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
+ rasterizer = true;
+ });
+
+#ifdef __linux__
+ if (!rasterizer && mapped) {
+ impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
+ }
+#endif
+
return mapped && ptr != nullptr;
}
+bool Memory::InvalidateSeparateHeap(void* fault_address) {
+#ifdef __linux__
+ return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
+#else
+ return false;
+#endif
+}
+
} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c1879e78f..dddfaf4a4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -86,7 +86,8 @@ public:
* @param perms The permissions to map the memory with.
*/
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
- Common::PhysicalAddress target, Common::MemoryPermission perms);
+ Common::PhysicalAddress target, Common::MemoryPermission perms,
+ bool separate_heap);
/**
* Unmaps a region of the emulated process address space.
@@ -95,7 +96,8 @@ public:
* @param base The address to begin unmapping at.
* @param size The amount of bytes to unmap.
*/
- void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size);
+ void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
+ bool separate_heap);
/**
* Protects a region of the emulated process address space with the new permissions.
@@ -486,6 +488,7 @@ public:
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
+ bool InvalidateSeparateHeap(void* fault_address);
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
private:
@@ -683,7 +686,8 @@ public:
} else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
- } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
+ } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
+ (FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 3fc4024dc..7bc5b5ae5 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
}
CheatEngine::~CheatEngine() {
- core_timing.UnscheduleEvent(event, 0);
+ core_timing.UnscheduleEvent(event);
}
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
- [this](std::uintptr_t user_data, s64 time,
+ [this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- FrameCallback(user_data, ns_late);
+ FrameCallback(ns_late);
return std::nullopt;
});
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
@@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
-void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
+void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
if (is_pending_reload.exchange(false)) {
vm.LoadProgram(cheats);
}
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 284abdd28..ced2168d1 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -70,7 +70,7 @@ public:
void Reload(std::vector<CheatEntry> reload_cheats);
private:
- void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void FrameCallback(std::chrono::nanoseconds ns_late);
DmntCheatVm vm;
CheatProcessMetadata metadata;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 98ebbbf32..9d42c726e 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -51,18 +51,17 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
: core_timing{core_timing_}, memory{memory_} {
- event = Core::Timing::CreateEvent(
- "MemoryFreezer::FrameCallback",
- [this](std::uintptr_t user_data, s64 time,
- std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
- FrameCallback(user_data, ns_late);
- return std::nullopt;
- });
+ event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
+ [this](s64 time, std::chrono::nanoseconds ns_late)
+ -> std::optional<std::chrono::nanoseconds> {
+ FrameCallback(ns_late);
+ return std::nullopt;
+ });
core_timing.ScheduleEvent(memory_freezer_ns, event);
}
Freezer::~Freezer() {
- core_timing.UnscheduleEvent(event, 0);
+ core_timing.UnscheduleEvent(event);
}
void Freezer::SetActive(bool is_active) {
@@ -159,7 +158,7 @@ Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
[address](const Entry& entry) { return entry.address == address; });
}
-void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
+void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) {
if (!IsActive()) {
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
return;
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 0d6df5217..2efbc11f3 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -77,7 +77,7 @@ private:
Entries::iterator FindEntry(VAddr address);
Entries::const_iterator FindEntry(VAddr address) const;
- void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void FrameCallback(std::chrono::nanoseconds ns_late);
void FillEntryReads();
std::atomic_bool active{false};
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index d9f99148b..20de91ff4 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -403,59 +403,63 @@ void Config::SavePlayerValues(const std::size_t player_index) {
// No custom profile selected
return;
}
- WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
- std::make_optional(std::string("")));
+ WriteStringSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
+ std::make_optional(std::string("")));
}
- WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
- std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
+ WriteIntegerSetting(
+ std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
+ std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
- WriteSetting(std::string(player_prefix).append("connected"), player.connected,
- std::make_optional(player_index == 0));
- WriteSetting(std::string(player_prefix).append("vibration_enabled"),
- player.vibration_enabled, std::make_optional(true));
- WriteSetting(std::string(player_prefix).append("vibration_strength"),
- player.vibration_strength, std::make_optional(100));
- WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
- std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
- WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
- std::make_optional(Settings::JOYCON_BODY_NEON_RED));
- WriteSetting(std::string(player_prefix).append("button_color_left"),
- player.button_color_left,
- std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
- WriteSetting(std::string(player_prefix).append("button_color_right"),
- player.button_color_right,
- std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
+ WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected,
+ std::make_optional(player_index == 0));
+ WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"),
+ player.vibration_enabled, std::make_optional(true));
+ WriteIntegerSetting(std::string(player_prefix).append("vibration_strength"),
+ player.vibration_strength, std::make_optional(100));
+ WriteIntegerSetting(std::string(player_prefix).append("body_color_left"),
+ player.body_color_left,
+ std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
+ WriteIntegerSetting(std::string(player_prefix).append("body_color_right"),
+ player.body_color_right,
+ std::make_optional(Settings::JOYCON_BODY_NEON_RED));
+ WriteIntegerSetting(std::string(player_prefix).append("button_color_left"),
+ player.button_color_left,
+ std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
+ WriteIntegerSetting(std::string(player_prefix).append("button_color_right"),
+ player.button_color_right,
+ std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
}
}
void Config::SaveTouchscreenValues() {
const auto& touchscreen = Settings::values.touchscreen;
- WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
+ WriteBooleanSetting(std::string("touchscreen_enabled"), touchscreen.enabled,
+ std::make_optional(true));
- WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
- std::make_optional(static_cast<u32>(0)));
- WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
- std::make_optional(static_cast<u32>(15)));
- WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
- std::make_optional(static_cast<u32>(15)));
+ WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
+ std::make_optional(static_cast<u32>(0)));
+ WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
+ std::make_optional(static_cast<u32>(15)));
+ WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
+ std::make_optional(static_cast<u32>(15)));
}
void Config::SaveMotionTouchValues() {
BeginArray(std::string("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
SetArrayIndex(static_cast<int>(p));
- WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
- std::make_optional(std::string("default")));
+ WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
+ std::make_optional(std::string("default")));
BeginArray(std::string("entries"));
for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
++q) {
SetArrayIndex(static_cast<int>(q));
- WriteSetting(std::string("bind"),
- Settings::values.touch_from_button_maps[p].buttons[q]);
+ WriteStringSetting(std::string("bind"),
+ Settings::values.touch_from_button_maps[p].buttons[q]);
}
EndArray(); // entries
}
@@ -520,16 +524,16 @@ void Config::SaveCoreValues() {
void Config::SaveDataStorageValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
- WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
- std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
- WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
- std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
- WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
- std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
- WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
- std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
- WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
- std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
+ WriteStringSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
+ std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
+ WriteStringSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
+ std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
+ WriteStringSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
+ std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
+ WriteStringSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
+ std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
+ WriteStringSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
+ std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
WriteCategory(Settings::Category::DataStorage);
@@ -540,7 +544,7 @@ void Config::SaveDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
- WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
+ WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteCategory(Settings::Category::Debugging);
WriteCategory(Settings::Category::DebuggingGraphics);
@@ -564,11 +568,13 @@ void Config::SaveDisabledAddOnValues() {
BeginArray(std::string(""));
for (const auto& elem : Settings::values.disabled_addons) {
SetArrayIndex(i);
- WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
+ WriteIntegerSetting(std::string("title_id"), elem.first,
+ std::make_optional(static_cast<u64>(0)));
BeginArray(std::string("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
SetArrayIndex(static_cast<int>(j));
- WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
+ WriteStringSetting(std::string("d"), elem.second[j],
+ std::make_optional(std::string("")));
}
EndArray(); // disabled
++i;
@@ -609,8 +615,8 @@ void Config::SaveRendererValues() {
void Config::SaveScreenshotValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
- WriteSetting(std::string("screenshot_path"),
- FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
+ WriteStringSetting(std::string("screenshot_path"),
+ FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
WriteCategory(Settings::Category::Screenshots);
EndGroup();
@@ -746,46 +752,59 @@ bool Config::Exists(const std::string& section, const std::string& key) const {
return !value.empty();
}
-template <typename Type>
-void Config::WriteSetting(const std::string& key, const Type& value,
- const std::optional<Type>& default_value,
- const std::optional<bool>& use_global) {
- std::string full_key = GetFullKey(key, false);
+void Config::WriteBooleanSetting(const std::string& key, const bool& value,
+ const std::optional<bool>& default_value,
+ const std::optional<bool>& use_global) {
+ std::optional<std::string> string_default = std::nullopt;
+ if (default_value.has_value()) {
+ string_default = std::make_optional(ToString(default_value.value()));
+ }
+ WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
+}
- std::string saved_value;
- std::string string_default;
- if constexpr (std::is_same_v<Type, std::string>) {
- saved_value.append(AdjustOutputString(value));
- if (default_value.has_value()) {
- string_default.append(AdjustOutputString(default_value.value()));
- }
- } else {
- saved_value.append(AdjustOutputString(ToString(value)));
- if (default_value.has_value()) {
- string_default.append(ToString(default_value.value()));
- }
+void Config::WriteDoubleSetting(const std::string& key, const double& value,
+ const std::optional<double>& default_value,
+ const std::optional<bool>& use_global) {
+ std::optional<std::string> string_default = std::nullopt;
+ if (default_value.has_value()) {
+ string_default = std::make_optional(ToString(default_value.value()));
}
+ WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
+}
- if (default_value.has_value() && use_global.has_value()) {
+void Config::WriteStringSetting(const std::string& key, const std::string& value,
+ const std::optional<std::string>& default_value,
+ const std::optional<bool>& use_global) {
+ std::optional string_default = default_value;
+ if (default_value.has_value()) {
+ string_default.value().append(AdjustOutputString(default_value.value()));
+ }
+ WritePreparedSetting(key, AdjustOutputString(value), string_default, use_global);
+}
+
+void Config::WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
+ const std::optional<std::string>& adjusted_default_value,
+ const std::optional<bool>& use_global) {
+ std::string full_key = GetFullKey(key, false);
+ if (adjusted_default_value.has_value() && use_global.has_value()) {
if (!global) {
- WriteSettingInternal(std::string(full_key).append("\\global"),
- ToString(use_global.value()));
+ WriteString(std::string(full_key).append("\\global"), ToString(use_global.value()));
}
if (global || use_global.value() == false) {
- WriteSettingInternal(std::string(full_key).append("\\default"),
- ToString(string_default == saved_value));
- WriteSettingInternal(full_key, saved_value);
+ WriteString(std::string(full_key).append("\\default"),
+ ToString(adjusted_default_value == adjusted_value));
+ WriteString(full_key, adjusted_value);
}
- } else if (default_value.has_value() && !use_global.has_value()) {
- WriteSettingInternal(std::string(full_key).append("\\default"),
- ToString(string_default == saved_value));
- WriteSettingInternal(full_key, saved_value);
+ } else if (adjusted_default_value.has_value() && !use_global.has_value()) {
+ WriteString(std::string(full_key).append("\\default"),
+ ToString(adjusted_default_value == adjusted_value));
+ WriteString(full_key, adjusted_value);
} else {
- WriteSettingInternal(full_key, saved_value);
+ WriteString(full_key, adjusted_value);
}
}
-void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
+void Config::WriteString(const std::string& key, const std::string& value) {
config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
}
@@ -861,17 +880,18 @@ void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
std::string key = AdjustKey(setting->GetLabel());
if (setting->Switchable()) {
if (!global) {
- WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
+ WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
}
if (global || !setting->UsingGlobal()) {
- WriteSetting(std::string(key).append("\\default"),
- setting->ToString() == setting->DefaultToString());
- WriteSetting(key, setting->ToString());
+ auto value = global ? setting->ToStringGlobal() : setting->ToString();
+ WriteBooleanSetting(std::string(key).append("\\default"),
+ value == setting->DefaultToString());
+ WriteStringSetting(key, value);
}
} else if (global) {
- WriteSetting(std::string(key).append("\\default"),
- setting->ToString() == setting->DefaultToString());
- WriteSetting(key, setting->ToString());
+ WriteBooleanSetting(std::string(key).append("\\default"),
+ setting->ToString() == setting->DefaultToString());
+ WriteStringSetting(key, setting->ToString());
}
}
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index b3812af17..b01631649 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -154,11 +154,26 @@ protected:
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
- template <typename Type = int>
- void WriteSetting(const std::string& key, const Type& value,
- const std::optional<Type>& default_value = std::nullopt,
- const std::optional<bool>& use_global = std::nullopt);
- void WriteSettingInternal(const std::string& key, const std::string& value);
+ void WriteBooleanSetting(const std::string& key, const bool& value,
+ const std::optional<bool>& default_value = std::nullopt,
+ const std::optional<bool>& use_global = std::nullopt);
+ void WriteDoubleSetting(const std::string& key, const double& value,
+ const std::optional<double>& default_value = std::nullopt,
+ const std::optional<bool>& use_global = std::nullopt);
+ void WriteStringSetting(const std::string& key, const std::string& value,
+ const std::optional<std::string>& default_value = std::nullopt,
+ const std::optional<bool>& use_global = std::nullopt);
+ template <typename T>
+ std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
+ const std::string& key, const T& value,
+ const std::optional<T>& default_value = std::nullopt,
+ const std::optional<bool>& use_global = std::nullopt) {
+ std::optional<std::string> string_default = std::nullopt;
+ if (default_value.has_value()) {
+ string_default = std::make_optional(ToString(default_value.value()));
+ }
+ WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
+ }
void ReadCategory(Settings::Category category);
void WriteCategory(Settings::Category category);
@@ -175,8 +190,10 @@ protected:
return value_ ? "true" : "false";
} else if constexpr (std::is_same_v<T, u64>) {
return std::to_string(static_cast<u64>(value_));
- } else {
+ } else if constexpr (std::is_same_v<T, s64>) {
return std::to_string(static_cast<s64>(value_));
+ } else {
+ return std::to_string(value_);
}
}
@@ -197,9 +214,13 @@ protected:
const bool global;
private:
- inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
- '|', ';', '\'', '\"', ',', '<', '.',
- '>', '?', '`', '~', '='};
+ void WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
+ const std::optional<std::string>& adjusted_default_value,
+ const std::optional<bool>& use_global);
+ void WriteString(const std::string& key, const std::string& value);
+
+ inline static std::array<char, 18> special_characters = {
+ '!', '#', '$', '%', '^', '&', '*', '|', ';', '\'', '\"', ',', '<', '>', '?', '`', '~', '='};
struct ConfigArray {
std::string name;
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index e5a78a914..feca5105f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -74,6 +74,11 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
case IR::Attribute::ClipDistance7: {
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
const u32 index{static_cast<u32>(attr) - base};
+ if (index >= ctx.profile.max_user_clip_distances) {
+ LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
+ ctx.profile.max_user_clip_distances);
+ return std::nullopt;
+ }
const Id clip_num{ctx.Const(index)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 22ceca19c..800754554 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -214,16 +214,16 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
}
}
-Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
+std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
if (!index.IsImmediate() || index.U32() != 0) {
throw NotImplementedException("Indirect image indexing");
}
if (info.type == TextureType::Buffer) {
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
- return ctx.OpLoad(def.image_type, def.id);
+ return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
} else {
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
- return ctx.OpLoad(def.image_type, def.id);
+ return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
}
}
@@ -566,13 +566,23 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
return ctx.ConstantNull(ctx.U32[4]);
}
- return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
- Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
+ const auto [image, is_integer] = Image(ctx, index, info);
+ const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
+ Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
+ result_type, image, coords, std::nullopt, std::span<const Id>{})};
+ if (!is_integer) {
+ color = ctx.OpBitcast(ctx.U32[4], color);
+ }
+ return color;
}
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
- ctx.OpImageWrite(Image(ctx, index, info), coords, color);
+ const auto [image, is_integer] = Image(ctx, index, info);
+ if (!is_integer) {
+ color = ctx.OpBitcast(ctx.F32[4], color);
+ }
+ ctx.OpImageWrite(image, coords, color);
}
Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index eb3cc23cc..0442adc83 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -74,20 +74,19 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
throw InvalidArgument("Invalid image format {}", format);
}
-Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
+Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
const spv::ImageFormat format{GetImageFormat(desc.format)};
- const Id type{ctx.U32[1]};
switch (desc.type) {
case TextureType::Color1D:
- return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
case TextureType::ColorArray1D:
- return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
case TextureType::Color2D:
- return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
case TextureType::ColorArray2D:
- return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format);
case TextureType::Color3D:
- return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
case TextureType::Buffer:
throw NotImplementedException("Image buffer");
default:
@@ -97,9 +96,9 @@ Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
}
Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
- spv::StorageClass storage_class) {
+ spv::StorageClass storage_class, std::optional<Id> initializer = std::nullopt) {
const Id pointer_type{ctx.TypePointer(storage_class, type)};
- const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)};
+ const Id id{ctx.AddGlobalVariable(pointer_type, storage_class, initializer)};
if (builtin) {
ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
}
@@ -145,11 +144,12 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
}
Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
- std::optional<spv::BuiltIn> builtin = std::nullopt) {
+ std::optional<spv::BuiltIn> builtin = std::nullopt,
+ std::optional<Id> initializer = std::nullopt) {
if (invocations && ctx.stage == Stage::TessellationControl) {
type = ctx.TypeArray(type, ctx.Const(*invocations));
}
- return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
+ return DefineVariable(ctx, type, builtin, spv::StorageClass::Output, initializer);
}
void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
@@ -812,10 +812,14 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
labels.push_back(OpLabel());
}
if (info.stores.ClipDistances()) {
- literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
- labels.push_back(OpLabel());
- literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
- labels.push_back(OpLabel());
+ if (profile.max_user_clip_distances >= 4) {
+ literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
+ labels.push_back(OpLabel());
+ }
+ if (profile.max_user_clip_distances >= 8) {
+ literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
+ labels.push_back(OpLabel());
+ }
}
OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
OpSwitch(compare_index, default_label, literals, labels);
@@ -844,17 +848,21 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
++label_index;
}
if (info.stores.ClipDistances()) {
- AddLabel(labels[label_index]);
- const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
- OpStore(pointer, store_value);
- OpReturn();
- ++label_index;
- AddLabel(labels[label_index]);
- const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
- const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)};
- OpStore(pointer2, store_value);
- OpReturn();
- ++label_index;
+ if (profile.max_user_clip_distances >= 4) {
+ AddLabel(labels[label_index]);
+ const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
+ OpStore(pointer, store_value);
+ OpReturn();
+ ++label_index;
+ }
+ if (profile.max_user_clip_distances >= 8) {
+ AddLabel(labels[label_index]);
+ const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
+ const Id pointer{OpAccessChain(output_f32, clip_distances, fixed_index)};
+ OpStore(pointer, store_value);
+ OpReturn();
+ ++label_index;
+ }
}
AddLabel(end_block);
OpUnreachable();
@@ -1273,7 +1281,9 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
throw NotImplementedException("Array of image buffers");
}
const spv::ImageFormat format{GetImageFormat(desc.format)};
- const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
+ const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
+ const Id image_type{
+ TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding);
@@ -1283,6 +1293,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
.id = id,
.image_type = image_type,
.count = desc.count,
+ .is_integer = desc.is_integer,
});
if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id);
@@ -1327,7 +1338,8 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
if (desc.count != 1) {
throw NotImplementedException("Array of images");
}
- const Id image_type{ImageType(*this, desc)};
+ const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
+ const Id image_type{ImageType(*this, desc, sampled_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding);
@@ -1337,6 +1349,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
.id = id,
.image_type = image_type,
.count = desc.count,
+ .is_integer = desc.is_integer,
});
if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id);
@@ -1528,8 +1541,16 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
if (stage == Stage::Fragment) {
throw NotImplementedException("Storing ClipDistance in fragment stage");
}
- const Id type{TypeArray(F32[1], Const(8U))};
- clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
+ if (profile.max_user_clip_distances > 0) {
+ const u32 used{std::min(profile.max_user_clip_distances, 8u)};
+ const std::array<Id, 8> zero{f32_zero_value, f32_zero_value, f32_zero_value,
+ f32_zero_value, f32_zero_value, f32_zero_value,
+ f32_zero_value, f32_zero_value};
+ const Id type{TypeArray(F32[1], Const(used))};
+ const Id initializer{ConstantComposite(type, std::span(zero).subspan(0, used))};
+ clip_distances =
+ DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance, initializer);
+ }
}
if (info.stores[IR::Attribute::Layer] &&
(profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 1aa79863d..56019ad89 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -47,12 +47,14 @@ struct ImageBufferDefinition {
Id id;
Id image_type;
u32 count;
+ bool is_integer;
};
struct ImageDefinition {
Id id;
Id image_type;
u32 count;
+ bool is_integer;
};
struct UniformDefinitions {
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 15285ab0a..e30bf094a 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -24,6 +24,8 @@ public:
[[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
+ [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
+
[[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
[[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 70292686f..cb82a326c 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
}
for (size_t index = 0; index < 8; ++index) {
const u16 mask{header.vtg.omap_systemc.clip_distances};
- info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
+ const bool used{((mask >> index) & 1) != 0};
+ info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
+ if (used) {
+ info.used_clip_distances = static_cast<u32>(index) + 1;
+ }
}
info.stores.Set(IR::Attribute::PrimitiveId,
header.vtg.omap_systemb.primitive_array_id != 0);
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index ec12c843a..e4a73a360 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -815,6 +815,15 @@ bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coor
return true;
}
+void ConvertDerivatives(std::array<IR::Value, 3>& results, IR::IREmitter& ir) {
+ for (size_t i = 0; i < 3; i++) {
+ if (results[i].Type() == IR::Type::U32) {
+ results[i] = results[i].IsImmediate() ? ir.Imm32(Common::BitCast<f32>(results[i].U32()))
+ : ir.BitCast<IR::F32>(IR::U32(results[i]));
+ }
+ }
+}
+
void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>();
auto orig_opcode = inst.GetOpcode();
@@ -831,12 +840,14 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
if (!offset.IsImmediate()) {
return;
}
+ IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::Inst* const inst2 = coords.InstRecursive();
std::array<std::array<IR::Value, 3>, 3> results_matrix;
for (size_t i = 0; i < 3; i++) {
if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
return;
}
+ ConvertDerivatives(results_matrix[i], ir);
}
IR::F32 lod_clamp{};
if (info.has_lod_clamp != 0) {
@@ -846,7 +857,6 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
lod_clamp = IR::F32{bias_lc};
}
}
- IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::Value new_coords =
ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]);
IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index d374c976a..100437f0e 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -372,6 +372,10 @@ TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAdd
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
}
+bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
+ return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
+}
+
class Descriptors {
public:
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -403,6 +407,7 @@ public:
})};
image_buffer_descriptors[index].is_written |= desc.is_written;
image_buffer_descriptors[index].is_read |= desc.is_read;
+ image_buffer_descriptors[index].is_integer |= desc.is_integer;
return index;
}
@@ -432,6 +437,7 @@ public:
})};
image_descriptors[index].is_written |= desc.is_written;
image_descriptors[index].is_read |= desc.is_read;
+ image_descriptors[index].is_integer |= desc.is_integer;
return index;
}
@@ -469,6 +475,20 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
}
+bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
+ switch (pixel_format) {
+ case TexturePixelFormat::A8B8G8R8_SNORM:
+ case TexturePixelFormat::R8G8_SNORM:
+ case TexturePixelFormat::R8_SNORM:
+ case TexturePixelFormat::R16G16B16A16_SNORM:
+ case TexturePixelFormat::R16G16_SNORM:
+ case TexturePixelFormat::R16_SNORM:
+ return true;
+ default:
+ return false;
+ }
+}
+
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
@@ -587,11 +607,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
}
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
+ const bool is_integer{IsTexturePixelFormatInteger(env, cbuf)};
if (flags.type == TextureType::Buffer) {
index = descriptors.Add(ImageBufferDescriptor{
.format = flags.image_format,
.is_written = is_written,
.is_read = is_read,
+ .is_integer = is_integer,
.cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset,
.count = cbuf.count,
@@ -603,6 +625,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.format = flags.image_format,
.is_written = is_written,
.is_read = is_read,
+ .is_integer = is_integer,
.cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset,
.count = cbuf.count,
@@ -658,7 +681,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
flags.type == TextureType::Buffer) {
const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
- if (pixel_format != TexturePixelFormat::OTHER) {
+ if (IsPixelFormatSNorm(pixel_format)) {
PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
}
}
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 66901a965..7578d41cc 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -87,6 +87,8 @@ struct Profile {
bool has_broken_robust{};
u64 min_ssbo_alignment{};
+
+ u32 max_user_clip_distances{};
};
} // namespace Shader
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index b4b4afd37..ed13e6820 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -35,14 +35,109 @@ enum class TextureType : u32 {
};
constexpr u32 NUM_TEXTURE_TYPES = 9;
-enum class TexturePixelFormat : u32 {
+enum class TexturePixelFormat {
+ A8B8G8R8_UNORM,
A8B8G8R8_SNORM,
+ A8B8G8R8_SINT,
+ A8B8G8R8_UINT,
+ R5G6B5_UNORM,
+ B5G6R5_UNORM,
+ A1R5G5B5_UNORM,
+ A2B10G10R10_UNORM,
+ A2B10G10R10_UINT,
+ A2R10G10B10_UNORM,
+ A1B5G5R5_UNORM,
+ A5B5G5R1_UNORM,
+ R8_UNORM,
R8_SNORM,
- R8G8_SNORM,
+ R8_SINT,
+ R8_UINT,
+ R16G16B16A16_FLOAT,
+ R16G16B16A16_UNORM,
R16G16B16A16_SNORM,
- R16G16_SNORM,
+ R16G16B16A16_SINT,
+ R16G16B16A16_UINT,
+ B10G11R11_FLOAT,
+ R32G32B32A32_UINT,
+ BC1_RGBA_UNORM,
+ BC2_UNORM,
+ BC3_UNORM,
+ BC4_UNORM,
+ BC4_SNORM,
+ BC5_UNORM,
+ BC5_SNORM,
+ BC7_UNORM,
+ BC6H_UFLOAT,
+ BC6H_SFLOAT,
+ ASTC_2D_4X4_UNORM,
+ B8G8R8A8_UNORM,
+ R32G32B32A32_FLOAT,
+ R32G32B32A32_SINT,
+ R32G32_FLOAT,
+ R32G32_SINT,
+ R32_FLOAT,
+ R16_FLOAT,
+ R16_UNORM,
R16_SNORM,
- OTHER
+ R16_UINT,
+ R16_SINT,
+ R16G16_UNORM,
+ R16G16_FLOAT,
+ R16G16_UINT,
+ R16G16_SINT,
+ R16G16_SNORM,
+ R32G32B32_FLOAT,
+ A8B8G8R8_SRGB,
+ R8G8_UNORM,
+ R8G8_SNORM,
+ R8G8_SINT,
+ R8G8_UINT,
+ R32G32_UINT,
+ R16G16B16X16_FLOAT,
+ R32_UINT,
+ R32_SINT,
+ ASTC_2D_8X8_UNORM,
+ ASTC_2D_8X5_UNORM,
+ ASTC_2D_5X4_UNORM,
+ B8G8R8A8_SRGB,
+ BC1_RGBA_SRGB,
+ BC2_SRGB,
+ BC3_SRGB,
+ BC7_SRGB,
+ A4B4G4R4_UNORM,
+ G4R4_UNORM,
+ ASTC_2D_4X4_SRGB,
+ ASTC_2D_8X8_SRGB,
+ ASTC_2D_8X5_SRGB,
+ ASTC_2D_5X4_SRGB,
+ ASTC_2D_5X5_UNORM,
+ ASTC_2D_5X5_SRGB,
+ ASTC_2D_10X8_UNORM,
+ ASTC_2D_10X8_SRGB,
+ ASTC_2D_6X6_UNORM,
+ ASTC_2D_6X6_SRGB,
+ ASTC_2D_10X6_UNORM,
+ ASTC_2D_10X6_SRGB,
+ ASTC_2D_10X5_UNORM,
+ ASTC_2D_10X5_SRGB,
+ ASTC_2D_10X10_UNORM,
+ ASTC_2D_10X10_SRGB,
+ ASTC_2D_12X10_UNORM,
+ ASTC_2D_12X10_SRGB,
+ ASTC_2D_12X12_UNORM,
+ ASTC_2D_12X12_SRGB,
+ ASTC_2D_8X6_UNORM,
+ ASTC_2D_8X6_SRGB,
+ ASTC_2D_6X5_UNORM,
+ ASTC_2D_6X5_SRGB,
+ E5B9G9R9_FLOAT,
+ D32_FLOAT,
+ D16_UNORM,
+ X8_D24_UNORM,
+ S8_UINT,
+ D24_UNORM_S8_UINT,
+ S8_UINT_D24_UNORM,
+ D32_FLOAT_S8_UINT,
};
enum class ImageFormat : u32 {
@@ -97,6 +192,7 @@ struct ImageBufferDescriptor {
ImageFormat format;
bool is_written;
bool is_read;
+ bool is_integer;
u32 cbuf_index;
u32 cbuf_offset;
u32 count;
@@ -129,6 +225,7 @@ struct ImageDescriptor {
ImageFormat format;
bool is_written;
bool is_read;
+ bool is_integer;
u32 cbuf_index;
u32 cbuf_offset;
u32 count;
@@ -227,6 +324,8 @@ struct Info {
bool requires_layer_emulation{};
IR::Attribute emulated_layer{};
+ u32 used_clip_distances{};
+
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
constant_buffer_descriptors;
boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index 1a28e862b..cb040c942 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -12,6 +12,7 @@ using namespace Common::Literals;
static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
static constexpr size_t BACKING_SIZE = 4_GiB;
static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
+static constexpr auto HEAP = false;
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
@@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
TEST_CASE("HostMemory: Simple map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x5000, 0x8000, 0x1000, PERMS);
+ mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[0] = 50;
@@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x5000, 0x3000, 0x2000, PERMS);
- mem.Map(0x8000, 0x4000, 0x1000, PERMS);
+ mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
+ mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
@@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
TEST_CASE("HostMemory: Simple unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x5000, 0x3000, 0x2000, PERMS);
+ mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[75] = 50;
REQUIRE(data[75] == 50);
- mem.Unmap(0x5000, 0x2000);
+ mem.Unmap(0x5000, 0x2000, HEAP);
}
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x5000, 0x3000, 0x2000, PERMS);
+ mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
data[0] = 50;
REQUIRE(data[0] == 50);
- mem.Unmap(0x5000, 0x2000);
+ mem.Unmap(0x5000, 0x2000, HEAP);
- mem.Map(0x5000, 0x3000, 0x2000, PERMS);
+ mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
REQUIRE(data[0] == 50);
- mem.Map(0x7000, 0x2000, 0x5000, PERMS);
+ mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
REQUIRE(data[0x3000] == 50);
}
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x0000, 0, 0x20000, PERMS);
- mem.Unmap(0x0000, 0x4000);
- mem.Map(0x1000, 0, 0x2000, PERMS);
- mem.Map(0x3000, 0, 0x1000, PERMS);
- mem.Map(0, 0, 0x1000, PERMS);
+ mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
+ mem.Unmap(0x0000, 0x4000, HEAP);
+ mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
+ mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
+ mem.Map(0, 0, 0x1000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Full unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x8000, 0, 0x4000, PERMS);
- mem.Unmap(0x8000, 0x4000);
- mem.Map(0x6000, 0, 0x16000, PERMS);
+ mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x8000, 0x4000, HEAP);
+ mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x0000, 0, 0x4000, PERMS);
- mem.Unmap(0x2000, 0x4000);
- mem.Map(0x2000, 0x80000, 0x4000, PERMS);
+ mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x2000, 0x4000, HEAP);
+ mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x8000, 0, 0x4000, PERMS);
- mem.Unmap(0x6000, 0x4000);
- mem.Map(0x8000, 0, 0x2000, PERMS);
+ mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x6000, 0x4000, HEAP);
+ mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x0000, 0, 0x4000, PERMS);
- mem.Map(0x4000, 0, 0x1b000, PERMS);
- mem.Unmap(0x3000, 0x1c000);
- mem.Map(0x3000, 0, 0x20000, PERMS);
+ mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
+ mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
+ mem.Unmap(0x3000, 0x1c000, HEAP);
+ mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x0000, 0, 0x4000, PERMS);
- mem.Map(0x4000, 0, 0x4000, PERMS);
- mem.Unmap(0x2000, 0x4000);
- mem.Map(0x2000, 0, 0x4000, PERMS);
+ mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
+ mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x2000, 0x4000, HEAP);
+ mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0, 0x4000, PERMS);
- mem.Map(0x8000, 0, 0x4000, PERMS);
- mem.Unmap(0x4000, 0x4000);
- mem.Map(0, 0, 0x4000, PERMS);
- mem.Map(0x4000, 0, 0x4000, PERMS);
+ mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
+ mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x4000, 0x4000, HEAP);
+ mem.Map(0, 0, 0x4000, PERMS, HEAP);
+ mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Unmap to right", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0, 0x4000, PERMS);
- mem.Map(0x8000, 0, 0x4000, PERMS);
- mem.Unmap(0x8000, 0x4000);
- mem.Map(0x8000, 0, 0x4000, PERMS);
+ mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
+ mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
+ mem.Unmap(0x8000, 0x4000, HEAP);
+ mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
}
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0x10000, 0x4000, PERMS);
+ mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x1000] = 17;
- mem.Unmap(0x6000, 0x2000);
+ mem.Unmap(0x6000, 0x2000, HEAP);
REQUIRE(ptr[0x1000] == 17);
}
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0x10000, 0x4000, PERMS);
+ mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x3000] = 19;
ptr[0x3fff] = 12;
- mem.Unmap(0x4000, 0x2000);
+ mem.Unmap(0x4000, 0x2000, HEAP);
REQUIRE(ptr[0x3000] == 19);
REQUIRE(ptr[0x3fff] == 12);
@@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0x10000, 0x4000, PERMS);
+ mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x0000] = 19;
ptr[0x3fff] = 12;
- mem.Unmap(0x1000, 0x2000);
+ mem.Unmap(0x1000, 0x2000, HEAP);
REQUIRE(ptr[0x0000] == 19);
REQUIRE(ptr[0x3fff] == 12);
@@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
- mem.Map(0x4000, 0x10000, 0x2000, PERMS);
- mem.Map(0x6000, 0x20000, 0x2000, PERMS);
+ mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
+ mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
ptr[0x0000] = 19;
ptr[0x3fff] = 12;
- mem.Unmap(0x5000, 0x2000);
+ mem.Unmap(0x5000, 0x2000, HEAP);
REQUIRE(ptr[0x0000] == 19);
REQUIRE(ptr[0x3fff] == 12);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index f08afbf9a..81898a1d3 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -16,20 +16,16 @@
namespace {
// Numbers are chosen randomly to make sure the correct one is given.
-constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
std::array<s64, 5> delays{};
-
-std::bitset<CB_IDS.size()> callbacks_ran_flags;
+std::bitset<5> callbacks_ran_flags;
u64 expected_callback = 0;
template <unsigned int IDX>
-std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
+std::optional<std::chrono::nanoseconds> HostCallbackTemplate(s64 time,
std::chrono::nanoseconds ns_late) {
- static_assert(IDX < CB_IDS.size(), "IDX out of range");
+ static_assert(IDX < callbacks_ran_flags.size(), "IDX out of range");
callbacks_ran_flags.set(IDX);
- REQUIRE(CB_IDS[IDX] == user_data);
- REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
delays[IDX] = ns_late.count();
++expected_callback;
return std::nullopt;
@@ -76,7 +72,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
const u64 order = calls_order[i];
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
- core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
+ core_timing.ScheduleEvent(future_ns, events[order]);
}
/// test pause
REQUIRE(callbacks_ran_flags.none());
@@ -118,7 +114,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
for (std::size_t i = 0; i < events.size(); i++) {
const u64 order = calls_order[i];
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
- core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
+ core_timing.ScheduleEvent(future_ns, events[order]);
}
const u64 end = core_timing.GetGlobalTimeNs().count();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 592c28ba3..95ba4f76c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -586,14 +586,22 @@ void Maxwell3D::ProcessQueryCondition() {
}
void Maxwell3D::ProcessCounterReset() {
- switch (regs.clear_report_value) {
- case Regs::ClearReport::ZPassPixelCount:
- rasterizer->ResetCounter(VideoCommon::QueryType::ZPassPixelCount64);
- break;
- default:
- LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value);
- break;
- }
+ const auto query_type = [clear_report = regs.clear_report_value]() {
+ switch (clear_report) {
+ case Tegra::Engines::Maxwell3D::Regs::ClearReport::ZPassPixelCount:
+ return VideoCommon::QueryType::ZPassPixelCount64;
+ case Tegra::Engines::Maxwell3D::Regs::ClearReport::StreamingPrimitivesSucceeded:
+ return VideoCommon::QueryType::StreamingPrimitivesSucceeded;
+ case Tegra::Engines::Maxwell3D::Regs::ClearReport::PrimitivesGenerated:
+ return VideoCommon::QueryType::PrimitivesGenerated;
+ case Tegra::Engines::Maxwell3D::Regs::ClearReport::VtgPrimitivesOut:
+ return VideoCommon::QueryType::VtgPrimitivesOut;
+ default:
+ LOG_DEBUG(HW_GPU, "Unimplemented counter reset={}", clear_report);
+ return VideoCommon::QueryType::Payload;
+ }
+ }();
+ rasterizer->ResetCounter(query_type);
}
void Maxwell3D::ProcessSyncPoint() {
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 422d4d859..56fbff306 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -228,7 +228,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
memory_manager, src_operand.address, src_size, &read_buffer);
- Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
+ Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
@@ -292,7 +292,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
GPUVAddr dst_addr = regs.offset_out;
Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
memory_manager, src_addr, src_size, &read_buffer);
- Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite>
+ Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 046c8085e..46e853e04 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -327,12 +327,13 @@ public:
explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
+ const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
+
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU);
- if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
+ if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
Fallback(parameters);
return;
}
-
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = true;
params.is_indexed = false;
@@ -503,6 +504,8 @@ public:
maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
regs.transform_feedback.controls[0].stride, true);
+
+ maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
}
};
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 9fcaeeac7..a64404ce4 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -28,8 +28,11 @@
namespace VideoCore {
enum class QueryType {
SamplesPassed,
+ PrimitivesGenerated,
+ TfbPrimitivesWritten,
+ Count,
};
-constexpr std::size_t NumQueryTypes = 1;
+constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
} // namespace VideoCore
namespace VideoCommon {
@@ -44,15 +47,6 @@ public:
explicit CounterStreamBase(QueryCache& cache_, VideoCore::QueryType type_)
: cache{cache_}, type{type_} {}
- /// Updates the state of the stream, enabling or disabling as needed.
- void Update(bool enabled) {
- if (enabled) {
- Enable();
- } else {
- Disable();
- }
- }
-
/// Resets the stream to zero. It doesn't disable the query after resetting.
void Reset() {
if (current) {
@@ -80,7 +74,6 @@ public:
return current != nullptr;
}
-private:
/// Enables the stream.
void Enable() {
if (current) {
@@ -97,6 +90,7 @@ private:
last = std::exchange(current, nullptr);
}
+private:
QueryCache& cache;
const VideoCore::QueryType type;
@@ -112,8 +106,14 @@ public:
: rasterizer{rasterizer_},
// Use reinterpret_cast instead of static_cast as workaround for
// UBSan bug (https://github.com/llvm/llvm-project/issues/59060)
- cpu_memory{cpu_memory_}, streams{{CounterStream{reinterpret_cast<QueryCache&>(*this),
- VideoCore::QueryType::SamplesPassed}}} {
+ cpu_memory{cpu_memory_}, streams{{
+ {CounterStream{reinterpret_cast<QueryCache&>(*this),
+ VideoCore::QueryType::SamplesPassed}},
+ {CounterStream{reinterpret_cast<QueryCache&>(*this),
+ VideoCore::QueryType::PrimitivesGenerated}},
+ {CounterStream{reinterpret_cast<QueryCache&>(*this),
+ VideoCore::QueryType::TfbPrimitivesWritten}},
+ }} {
(void)slot_async_jobs.insert(); // Null value
}
@@ -157,12 +157,11 @@ public:
AsyncFlushQuery(query, timestamp, lock);
}
- /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
- void UpdateCounters() {
+ /// Enables all available GPU counters
+ void EnableCounters() {
std::unique_lock lock{mutex};
- if (maxwell3d) {
- const auto& regs = maxwell3d->regs;
- Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable);
+ for (auto& stream : streams) {
+ stream.Enable();
}
}
@@ -176,7 +175,7 @@ public:
void DisableStreams() {
std::unique_lock lock{mutex};
for (auto& stream : streams) {
- stream.Update(false);
+ stream.Disable();
}
}
@@ -353,7 +352,7 @@ private:
std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{};
std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes;
-};
+}; // namespace VideoCommon
template <class QueryCache, class HostCounter>
class HostCounterBase {
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index af1469147..49224ca85 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -173,5 +173,13 @@ public:
virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
virtual void ReleaseChannel(s32 channel_id) {}
+
+ /// Register the address as a Transform Feedback Object
+ virtual void RegisterTransformFeedback(GPUVAddr tfb_object_addr) {}
+
+ /// Returns true when the rasterizer has Draw Transform Feedback capabilities
+ virtual bool HasDrawTransformFeedback() {
+ return false;
+ }
};
} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index b787b6994..517ac14dd 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -376,4 +376,15 @@ void BufferCacheRuntime::BindImageBuffer(Buffer& buffer, u32 offset, u32 size, P
*image_handles++ = buffer.View(offset, size, format);
}
+void BufferCacheRuntime::BindTransformFeedbackObject(GPUVAddr tfb_object_addr) {
+ OGLTransformFeedback& tfb_object = tfb_objects[tfb_object_addr];
+ tfb_object.Create();
+ glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfb_object.handle);
+}
+
+GLuint BufferCacheRuntime::GetTransformFeedbackObject(GPUVAddr tfb_object_addr) {
+ ASSERT(tfb_objects.contains(tfb_object_addr));
+ return tfb_objects[tfb_object_addr].handle;
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 1e8708f59..2c18de166 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -5,6 +5,7 @@
#include <array>
#include <span>
+#include <unordered_map>
#include "common/common_types.h"
#include "video_core/buffer_cache/buffer_cache_base.h"
@@ -121,6 +122,9 @@ public:
void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
VideoCore::Surface::PixelFormat format);
+ void BindTransformFeedbackObject(GPUVAddr tfb_object_addr);
+ GLuint GetTransformFeedbackObject(GPUVAddr tfb_object_addr);
+
u64 GetDeviceMemoryUsage() const;
void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
@@ -233,6 +237,7 @@ private:
u32 index_buffer_offset = 0;
u64 device_access_memory;
+ std::unordered_map<GPUVAddr, OGLTransformFeedback> tfb_objects;
};
struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index ec142d48e..fef7360ed 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -18,16 +18,27 @@ namespace OpenGL {
namespace {
-constexpr std::array<GLenum, VideoCore::NumQueryTypes> QueryTargets = {GL_SAMPLES_PASSED};
-
constexpr GLenum GetTarget(VideoCore::QueryType type) {
- return QueryTargets[static_cast<std::size_t>(type)];
+ switch (type) {
+ case VideoCore::QueryType::SamplesPassed:
+ return GL_SAMPLES_PASSED;
+ case VideoCore::QueryType::PrimitivesGenerated:
+ return GL_PRIMITIVES_GENERATED;
+ case VideoCore::QueryType::TfbPrimitivesWritten:
+ return GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;
+ default:
+ break;
+ }
+ UNIMPLEMENTED_MSG("Query type {}", type);
+ return 0;
}
} // Anonymous namespace
QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
- : QueryCacheLegacy(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {}
+ : QueryCacheLegacy(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {
+ EnableCounters();
+}
QueryCache::~QueryCache() = default;
@@ -103,13 +114,13 @@ u64 CachedQuery::Flush([[maybe_unused]] bool async) {
auto& stream = cache->Stream(type);
const bool slice_counter = WaitPending() && stream.IsEnabled();
if (slice_counter) {
- stream.Update(false);
+ stream.Disable();
}
auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush();
if (slice_counter) {
- stream.Update(true);
+ stream.Enable();
}
return result;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4832c03c5..7a5fad735 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -51,6 +51,22 @@ constexpr size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16;
void oglEnable(GLenum cap, bool state) {
(state ? glEnable : glDisable)(cap);
}
+
+std::optional<VideoCore::QueryType> MaxwellToVideoCoreQuery(VideoCommon::QueryType type) {
+ switch (type) {
+ case VideoCommon::QueryType::PrimitivesGenerated:
+ case VideoCommon::QueryType::VtgPrimitivesOut:
+ return VideoCore::QueryType::PrimitivesGenerated;
+ case VideoCommon::QueryType::ZPassPixelCount64:
+ return VideoCore::QueryType::SamplesPassed;
+ case VideoCommon::QueryType::StreamingPrimitivesSucceeded:
+ // case VideoCommon::QueryType::StreamingByteCount:
+ // TODO: StreamingByteCount = StreamingPrimitivesSucceeded * num_verts * vert_stride
+ return VideoCore::QueryType::TfbPrimitivesWritten;
+ default:
+ return std::nullopt;
+ }
+}
} // Anonymous namespace
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -216,7 +232,6 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
SCOPE_EXIT({ gpu.TickWork(); });
gpu_memory->FlushCaching();
- query_cache.UpdateCounters();
GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
if (!pipeline) {
@@ -294,6 +309,13 @@ void RasterizerOpenGL::DrawIndirect() {
const auto& params = maxwell3d->draw_manager->GetIndirectParams();
buffer_cache.SetDrawIndirect(&params);
PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
+ if (params.is_byte_count) {
+ const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
+ const GLuint tfb_object =
+ buffer_cache_runtime.GetTransformFeedbackObject(tfb_object_base_addr);
+ glDrawTransformFeedback(primitive_mode, tfb_object);
+ return;
+ }
const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
const GLvoid* const gl_offset =
reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
@@ -334,7 +356,6 @@ void RasterizerOpenGL::DrawTexture() {
MICROPROFILE_SCOPE(OpenGL_Drawing);
SCOPE_EXIT({ gpu.TickWork(); });
- query_cache.UpdateCounters();
texture_cache.SynchronizeGraphicsDescriptors();
texture_cache.UpdateRenderTargets(false);
@@ -401,21 +422,28 @@ void RasterizerOpenGL::DispatchCompute() {
}
void RasterizerOpenGL::ResetCounter(VideoCommon::QueryType type) {
- if (type == VideoCommon::QueryType::ZPassPixelCount64) {
- query_cache.ResetCounter(VideoCore::QueryType::SamplesPassed);
+ const auto query_cache_type = MaxwellToVideoCoreQuery(type);
+ if (!query_cache_type.has_value()) {
+ UNIMPLEMENTED_IF_MSG(type != VideoCommon::QueryType::Payload, "Reset query type: {}", type);
+ return;
}
+ query_cache.ResetCounter(*query_cache_type);
}
void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) {
- if (type == VideoCommon::QueryType::ZPassPixelCount64) {
- if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) {
- query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, {gpu.GetTicks()});
- } else {
- query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, std::nullopt);
- }
- return;
+ const auto query_cache_type = MaxwellToVideoCoreQuery(type);
+ if (!query_cache_type.has_value()) {
+ return QueryFallback(gpu_addr, type, flags, payload, subreport);
}
+ const bool has_timeout = True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout);
+ const auto timestamp = has_timeout ? std::optional<u64>{gpu.GetTicks()} : std::nullopt;
+ query_cache.Query(gpu_addr, *query_cache_type, timestamp);
+}
+
+void RasterizerOpenGL::QueryFallback(GPUVAddr gpu_addr, VideoCommon::QueryType type,
+ VideoCommon::QueryPropertiesFlags flags, u32 payload,
+ u32 subreport) {
if (type != VideoCommon::QueryType::Payload) {
payload = 1u;
}
@@ -1350,6 +1378,10 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
query_cache.EraseChannel(channel_id);
}
+void RasterizerOpenGL::RegisterTransformFeedback(GPUVAddr tfb_object_addr) {
+ buffer_cache_runtime.BindTransformFeedbackObject(tfb_object_addr);
+}
+
AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_)
: buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ceffe1f1e..ce3460938 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -139,6 +139,12 @@ public:
void ReleaseChannel(s32 channel_id) override;
+ void RegisterTransformFeedback(GPUVAddr tfb_object_addr) override;
+
+ bool HasDrawTransformFeedback() override {
+ return true;
+ }
+
private:
static constexpr size_t MAX_TEXTURES = 192;
static constexpr size_t MAX_IMAGES = 48;
@@ -225,6 +231,9 @@ private:
/// End a transform feedback
void EndTransformFeedback();
+ void QueryFallback(GPUVAddr gpu_addr, VideoCommon::QueryType type,
+ VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport);
+
Tegra::GPU& gpu;
const Device& device;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index eae8fd110..1d2c9b70a 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -207,4 +207,21 @@ void OGLQuery::Release() {
handle = 0;
}
+void OGLTransformFeedback::Create() {
+ if (handle != 0)
+ return;
+
+ MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
+ glCreateTransformFeedbacks(1, &handle);
+}
+
+void OGLTransformFeedback::Release() {
+ if (handle == 0)
+ return;
+
+ MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
+ glDeleteTransformFeedbacks(1, &handle);
+ handle = 0;
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 77362acd2..6ca8227bd 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -323,4 +323,31 @@ public:
GLuint handle = 0;
};
+class OGLTransformFeedback final {
+public:
+ YUZU_NON_COPYABLE(OGLTransformFeedback);
+
+ OGLTransformFeedback() = default;
+
+ OGLTransformFeedback(OGLTransformFeedback&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
+
+ ~OGLTransformFeedback() {
+ Release();
+ }
+
+ OGLTransformFeedback& operator=(OGLTransformFeedback&& o) noexcept {
+ Release();
+ handle = std::exchange(o.handle, 0);
+ return *this;
+ }
+
+ /// Creates a new internal OpenGL resource and stores the handle
+ void Create();
+
+ /// Deletes the internal OpenGL resource
+ void Release();
+
+ GLuint handle = 0;
+};
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 26f2d0ea7..30df41b7d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
using VideoCommon::SerializePipeline;
using Context = ShaderContext::Context;
-constexpr u32 CACHE_VERSION = 9;
+constexpr u32 CACHE_VERSION = 10;
template <typename Container>
auto MakeSpan(Container& container) {
@@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
+ .max_user_clip_distances = 8,
},
host_info{
.support_float64 = true,
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5958f52f7..3c61799fa 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -78,8 +78,15 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
}
} // Anonymous namespace
-Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
- : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
+Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
+ : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {
+ if (runtime.device.HasNullDescriptor()) {
+ return;
+ }
+ device = &runtime.device;
+ buffer = runtime.CreateNullBuffer();
+ is_null = true;
+}
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
VAddr cpu_addr_, u64 size_bytes_)
@@ -93,8 +100,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
if (!device) {
- // Null buffer, return a null descriptor
+ // Null buffer supported, return a null descriptor
return VK_NULL_HANDLE;
+ } else if (is_null) {
+ // Null buffer not supported, adjust offset and size
+ offset = 0;
+ size = 0;
}
const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
return offset == view.offset && size == view.size && format == view.format;
@@ -563,22 +574,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
}
buffer_handles.push_back(handle);
}
+ const u32 device_max = device.GetMaxVertexInputBindings();
+ const u32 min_binding = std::min(bindings.min_index, device_max);
+ const u32 max_binding = std::min(bindings.max_index, device_max);
+ const u32 binding_count = max_binding - min_binding;
+ if (binding_count == 0) {
+ return;
+ }
if (device.IsExtExtendedDynamicStateSupported()) {
- scheduler.Record([this, bindings_ = std::move(bindings),
- buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
- cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
- std::min(bindings_.max_index - bindings_.min_index,
- device.GetMaxVertexInputBindings()),
- buffer_handles_.data(), bindings_.offsets.data(),
- bindings_.sizes.data(), bindings_.strides.data());
+ scheduler.Record([bindings_ = std::move(bindings),
+ buffer_handles_ = std::move(buffer_handles),
+ binding_count](vk::CommandBuffer cmdbuf) {
+ cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
+ bindings_.offsets.data(), bindings_.sizes.data(),
+ bindings_.strides.data());
});
} else {
- scheduler.Record([this, bindings_ = std::move(bindings),
- buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
- cmdbuf.BindVertexBuffers(bindings_.min_index,
- std::min(bindings_.max_index - bindings_.min_index,
- device.GetMaxVertexInputBindings()),
- buffer_handles_.data(), bindings_.offsets.data());
+ scheduler.Record([bindings_ = std::move(bindings),
+ buffer_handles_ = std::move(buffer_handles),
+ binding_count](vk::CommandBuffer cmdbuf) {
+ cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
+ bindings_.offsets.data());
});
}
}
@@ -622,9 +638,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
}
void BufferCacheRuntime::ReserveNullBuffer() {
- if (null_buffer) {
- return;
+ if (!null_buffer) {
+ null_buffer = CreateNullBuffer();
}
+}
+
+vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
VkBufferCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -639,15 +658,17 @@ void BufferCacheRuntime::ReserveNullBuffer() {
if (device.IsExtTransformFeedbackSupported()) {
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
- null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
+ vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
if (device.HasDebuggingToolAttached()) {
- null_buffer.SetObjectNameEXT("Null buffer");
+ ret.SetObjectNameEXT("Null buffer");
}
scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) {
cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
});
+
+ return ret;
}
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 0b3fbd6d0..dc300d7cb 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -63,6 +63,7 @@ private:
vk::Buffer buffer;
std::vector<BufferView> views;
VideoCommon::UsageTracker tracker;
+ bool is_null{};
};
class QuadArrayIndexBuffer;
@@ -151,6 +152,7 @@ private:
}
void ReserveNullBuffer();
+ vk::Buffer CreateNullBuffer();
const Device& device;
MemoryAllocator& memory_allocator;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 2a13b2a72..d1841198d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -54,7 +54,7 @@ using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
-constexpr u32 CACHE_VERSION = 10;
+constexpr u32 CACHE_VERSION = 11;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>
@@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
+ .max_user_clip_distances = device.GetMaxUserClipDistances(),
};
host_info = Shader::HostTranslateInfo{
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 5e7518d96..792ed9615 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -329,7 +329,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) {
// to account for that.
const bool is_suboptimal = swapchain.NeedsRecreation();
const bool size_changed =
- swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height;
+ swapchain.GetWidth() < frame->width || swapchain.GetHeight() < frame->height;
if (is_suboptimal || size_changed) {
RecreateSwapchain(frame);
}
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 078777cdd..95954ade7 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -289,12 +289,15 @@ public:
}
if (has_multi_queries) {
- size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
+ const size_t min_accumulation_limit =
+ std::min(first_accumulation_checkpoint, num_slots_used);
+ const size_t max_accumulation_limit =
+ std::max(last_accumulation_checkpoint, num_slots_used);
+ const size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
resolve_buffers.push_back(intermediary_buffer_index);
queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
*buffers[resolve_buffer_index], num_slots_used,
- std::min(first_accumulation_checkpoint, num_slots_used),
- last_accumulation_checkpoint);
+ min_accumulation_limit, max_accumulation_limit);
} else {
scheduler.RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 59829c88b..241fc34be 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -485,6 +485,10 @@ void RasterizerVulkan::DispatchCompute() {
}
void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) {
+ if (type != VideoCommon::QueryType::ZPassPixelCount64) {
+ LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type);
+ return;
+ }
query_cache.CounterReset(type);
}
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 4edbe5700..492440ac4 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -62,23 +62,9 @@ static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& en
}
static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
- switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
- entry.a_type, entry.srgb_conversion)) {
- case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM:
- return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
- case VideoCore::Surface::PixelFormat::R8_SNORM:
- return Shader::TexturePixelFormat::R8_SNORM;
- case VideoCore::Surface::PixelFormat::R8G8_SNORM:
- return Shader::TexturePixelFormat::R8G8_SNORM;
- case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
- return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
- case VideoCore::Surface::PixelFormat::R16G16_SNORM:
- return Shader::TexturePixelFormat::R16G16_SNORM;
- case VideoCore::Surface::PixelFormat::R16_SNORM:
- return Shader::TexturePixelFormat::R16_SNORM;
- default:
- return Shader::TexturePixelFormat::OTHER;
- }
+ return static_cast<Shader::TexturePixelFormat>(
+ PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
+ entry.a_type, entry.srgb_conversion));
}
static std::string_view StageToPrefix(Shader::Stage stage) {
@@ -398,6 +384,11 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
return result;
}
+bool GraphicsEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 GraphicsEnvironment::ReadViewportTransformState() {
const auto& regs{maxwell3d->regs};
viewport_transform_state = regs.viewport_scale_offset_enabled;
@@ -448,6 +439,11 @@ Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle
return result;
}
+bool ComputeEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 ComputeEnvironment::ReadViewportTransformState() {
return viewport_transform_state;
}
@@ -551,6 +547,11 @@ Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
return it->second;
}
+bool FileEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 FileEnvironment::ReadViewportTransformState() {
return viewport_transform_state;
}
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index b90f3d44e..6b372e336 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -115,6 +115,8 @@ public:
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ bool IsTexturePixelFormatInteger(u32 handle) override;
+
u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
@@ -139,6 +141,8 @@ public:
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ bool IsTexturePixelFormatInteger(u32 handle) override;
+
u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
@@ -171,6 +175,8 @@ public:
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
+
[[nodiscard]] u32 ReadViewportTransformState() override;
[[nodiscard]] u32 LocalMemorySize() const override;
diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp
index 3e26474a3..a018c6df4 100644
--- a/src/video_core/texture_cache/decode_bc.cpp
+++ b/src/video_core/texture_cache/decode_bc.cpp
@@ -60,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
}
template <auto decompress, PixelFormat pixel_format>
-void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
+void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
bool is_signed = false) {
const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
- const u32 block_width = std::min(extent.width, BLOCK_SIZE);
- const u32 block_height = std::min(extent.height, BLOCK_SIZE);
- const u32 pitch = extent.width * out_bpp;
+ const u32 block_size = BlockSize(pixel_format);
+ const u32 width = copy.image_extent.width;
+ const u32 height = copy.image_extent.height * copy.image_subresource.num_layers;
+ const u32 depth = copy.image_extent.depth;
+ const u32 block_width = std::min(width, BLOCK_SIZE);
+ const u32 block_height = std::min(height, BLOCK_SIZE);
+ const u32 pitch = width * out_bpp;
size_t input_offset = 0;
size_t output_offset = 0;
- for (u32 slice = 0; slice < extent.depth; ++slice) {
- for (u32 y = 0; y < extent.height; y += block_height) {
- size_t row_offset = 0;
- for (u32 x = 0; x < extent.width;
- x += block_width, row_offset += block_width * out_bpp) {
- const u8* src = input.data() + input_offset;
- u8* const dst = output.data() + output_offset + row_offset;
+ for (u32 slice = 0; slice < depth; ++slice) {
+ for (u32 y = 0; y < height; y += block_height) {
+ size_t src_offset = input_offset;
+ size_t dst_offset = output_offset;
+ for (u32 x = 0; x < width; x += block_width) {
+ const u8* src = input.data() + src_offset;
+ u8* const dst = output.data() + dst_offset;
if constexpr (IsSigned(pixel_format)) {
- decompress(src, dst, x, y, extent.width, extent.height, is_signed);
+ decompress(src, dst, x, y, width, height, is_signed);
} else {
- decompress(src, dst, x, y, extent.width, extent.height);
+ decompress(src, dst, x, y, width, height);
}
- input_offset += BlockSize(pixel_format);
+ src_offset += block_size;
+ dst_offset += block_width * out_bpp;
}
+ input_offset += copy.buffer_row_length * block_size / block_width;
output_offset += block_height * pitch;
}
}
}
-void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
+void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
VideoCore::Surface::PixelFormat pixel_format) {
switch (pixel_format) {
case PixelFormat::BC1_RGBA_UNORM:
case PixelFormat::BC1_RGBA_SRGB:
- DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent);
+ DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
break;
case PixelFormat::BC2_UNORM:
case PixelFormat::BC2_SRGB:
- DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
+ DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
break;
case PixelFormat::BC3_UNORM:
case PixelFormat::BC3_SRGB:
- DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
+ DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
break;
case PixelFormat::BC4_SNORM:
case PixelFormat::BC4_UNORM:
DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
- input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
+ input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
break;
case PixelFormat::BC5_SNORM:
case PixelFormat::BC5_UNORM:
DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
- input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
+ input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
break;
case PixelFormat::BC6H_SFLOAT:
case PixelFormat::BC6H_UFLOAT:
DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
- input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
+ input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
break;
case PixelFormat::BC7_SRGB:
case PixelFormat::BC7_UNORM:
- DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
+ DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
break;
default:
LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
diff --git a/src/video_core/texture_cache/decode_bc.h b/src/video_core/texture_cache/decode_bc.h
index 41d1ec0a3..4e3b9b8ac 100644
--- a/src/video_core/texture_cache/decode_bc.h
+++ b/src/video_core/texture_cache/decode_bc.h
@@ -13,7 +13,7 @@ namespace VideoCommon {
[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
-void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
+void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
VideoCore::Surface::PixelFormat pixel_format);
} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 15596c925..fcf70068e 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -837,6 +837,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
std::span<u8> output) {
const size_t guest_size_bytes = input.size_bytes();
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
+ const Extent2D tile_size = DefaultBlockSize(info.format);
const Extent3D size = info.size;
if (info.type == ImageType::Linear) {
@@ -847,7 +848,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
return {{
.buffer_offset = 0,
.buffer_size = guest_size_bytes,
- .buffer_row_length = info.pitch >> bpp_log2,
+ .buffer_row_length = info.pitch * tile_size.width >> bpp_log2,
.buffer_image_height = size.height,
.image_subresource =
{
@@ -862,7 +863,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
const LevelInfo level_info = MakeLevelInfo(info);
const s32 num_layers = info.resources.layers;
const s32 num_levels = info.resources.levels;
- const Extent2D tile_size = DefaultBlockSize(info.format);
const std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
@@ -926,8 +926,6 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
const auto input_offset = input.subspan(copy.buffer_offset);
copy.buffer_offset = output_offset;
- copy.buffer_row_length = mip_size.width;
- copy.buffer_image_height = mip_size.height;
const auto recompression_setting = Settings::values.astc_recompression.GetValue();
const bool astc = IsPixelFormatASTC(info.format);
@@ -972,16 +970,14 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
bpp_div;
output_offset += static_cast<u32>(copy.buffer_size);
} else {
- const Extent3D image_extent{
- .width = copy.image_extent.width,
- .height = copy.image_extent.height * copy.image_subresource.num_layers,
- .depth = copy.image_extent.depth,
- };
- DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
+ DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format);
output_offset += copy.image_extent.width * copy.image_extent.height *
copy.image_subresource.num_layers *
ConvertedBytesPerBlock(info.format);
}
+
+ copy.buffer_row_length = mip_size.width;
+ copy.buffer_image_height = mip_size.height;
}
}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 1fda0042d..727bbd98d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -695,6 +695,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
std::min(properties.properties.limits.maxVertexInputBindings, 16U);
}
+ if (is_turnip) {
+ LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
+ properties.properties.limits.maxVertexInputBindings = 32;
+ }
+
if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
LOG_INFO(Render_Vulkan,
"Removing extendedDynamicState2 due to missing extendedDynamicState");
@@ -750,10 +755,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
// The wanted format is not supported by hardware, search for alternatives
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
if (alternatives == nullptr) {
- ASSERT_MSG(false,
- "Format={} with usage={} and type={} has no defined alternatives and host "
- "hardware does not support it",
- wanted_format, wanted_usage, format_type);
+ LOG_ERROR(Render_Vulkan,
+ "Format={} with usage={} and type={} has no defined alternatives and host "
+ "hardware does not support it",
+ wanted_format, wanted_usage, format_type);
return wanted_format;
}
@@ -769,10 +774,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
}
// No alternatives found, panic
- ASSERT_MSG(false,
- "Format={} with usage={} and type={} is not supported by the host hardware and "
- "doesn't support any of the alternatives",
- wanted_format, wanted_usage, format_type);
+ LOG_ERROR(Render_Vulkan,
+ "Format={} with usage={} and type={} is not supported by the host hardware and "
+ "doesn't support any of the alternatives",
+ wanted_format, wanted_usage, format_type);
return wanted_format;
}
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 4f3846345..701817086 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -665,6 +665,10 @@ public:
return properties.properties.limits.maxViewports;
}
+ u32 GetMaxUserClipDistances() const {
+ return properties.properties.limits.maxClipDistances;
+ }
+
bool SupportsConditionalBarriers() const {
return supports_conditional_barriers;
}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 70cf14afa..074aed964 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -246,7 +246,9 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
.objectHandle = reinterpret_cast<u64>(handle),
.pObjectName = name,
};
- Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
+ if (dld->vkSetDebugUtilsObjectNameEXT) {
+ Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
+ }
}
} // Anonymous namespace
@@ -377,6 +379,8 @@ const char* ToString(VkResult result) noexcept {
return "VK_OPERATION_DEFERRED_KHR";
case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
return "VK_OPERATION_NOT_DEFERRED_KHR";
+ case VkResult::VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR:
+ return "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
case VkResult::VK_RESULT_MAX_ENUM:
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index fd6bebf0f..0836bcb7e 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -106,32 +106,30 @@ ConfigureGraphics::ConfigureGraphics(
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
- PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
+ PopulateVSyncModeSelection(false); //< must happen after UpdateAPILayout
// VSync setting needs to be determined after populating the VSync combobox
- if (Settings::IsConfiguringGlobal()) {
- const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
- const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
- int index{};
- for (const auto mode : vsync_mode_combobox_enum_map) {
- if (mode == vsync_mode) {
- break;
- }
- index++;
- }
- if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
- vsync_mode_combobox->setCurrentIndex(index);
+ const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
+ const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
+ int index{};
+ for (const auto mode : vsync_mode_combobox_enum_map) {
+ if (mode == vsync_mode) {
+ break;
}
+ index++;
+ }
+ if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
+ vsync_mode_combobox->setCurrentIndex(index);
}
connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
UpdateAPILayout();
- PopulateVSyncModeSelection();
+ PopulateVSyncModeSelection(false);
});
connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int device) {
UpdateDeviceSelection(device);
- PopulateVSyncModeSelection();
+ PopulateVSyncModeSelection(false);
});
connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int backend) { UpdateShaderBackendSelection(backend); });
@@ -147,8 +145,9 @@ ConfigureGraphics::ConfigureGraphics(
const auto& update_screenshot_info = [this, &builder]() {
const auto& combobox_enumerations = builder.ComboboxTranslations().at(
Settings::EnumMetadata<Settings::AspectRatio>::Index());
- const auto index = aspect_ratio_combobox->currentIndex();
- const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first);
+ const auto ratio_index = aspect_ratio_combobox->currentIndex();
+ const auto ratio =
+ static_cast<Settings::AspectRatio>(combobox_enumerations[ratio_index].first);
const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
@@ -174,11 +173,7 @@ ConfigureGraphics::ConfigureGraphics(
}
}
-void ConfigureGraphics::PopulateVSyncModeSelection() {
- if (!Settings::IsConfiguringGlobal()) {
- return;
- }
-
+void ConfigureGraphics::PopulateVSyncModeSelection(bool use_setting) {
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
if (backend == Settings::RendererBackend::Null) {
vsync_mode_combobox->setEnabled(false);
@@ -189,8 +184,9 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
const int current_index = //< current selected vsync mode from combobox
vsync_mode_combobox->currentIndex();
const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
- current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
- : vsync_mode_combobox_enum_map[current_index];
+ current_index == -1 || use_setting
+ ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
+ : vsync_mode_combobox_enum_map[current_index];
int index{};
const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
@@ -214,6 +210,23 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
}
index++;
}
+
+ if (!Settings::IsConfiguringGlobal()) {
+ vsync_restore_global_button->setVisible(!Settings::values.vsync_mode.UsingGlobal());
+
+ const Settings::VSyncMode global_vsync_mode = Settings::values.vsync_mode.GetValue(true);
+ vsync_restore_global_button->setEnabled(
+ (backend == Settings::RendererBackend::OpenGL &&
+ (global_vsync_mode == Settings::VSyncMode::Immediate ||
+ global_vsync_mode == Settings::VSyncMode::Fifo)) ||
+ backend == Settings::RendererBackend::Vulkan);
+ }
+}
+
+void ConfigureGraphics::UpdateVsyncSetting() const {
+ const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
+ const auto vsync_mode = PresentModeToSetting(mode);
+ Settings::values.vsync_mode.SetValue(vsync_mode);
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -299,6 +312,33 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.vsync_mode.Id()) {
// Keep track of vsync_mode's combobox so we can populate it
vsync_mode_combobox = widget->combobox;
+
+ // Since vsync is populated at runtime, we have to manually set up the button for
+ // restoring the global setting.
+ if (!Settings::IsConfiguringGlobal()) {
+ QPushButton* restore_button =
+ ConfigurationShared::Widget::CreateRestoreGlobalButton(
+ Settings::values.vsync_mode.UsingGlobal(), widget);
+ restore_button->setEnabled(true);
+ widget->layout()->addWidget(restore_button);
+
+ QObject::connect(restore_button, &QAbstractButton::clicked,
+ [restore_button, this](bool) {
+ Settings::values.vsync_mode.SetGlobal(true);
+ PopulateVSyncModeSelection(true);
+
+ restore_button->setVisible(false);
+ });
+
+ std::function<void()> set_non_global = [restore_button, this]() {
+ Settings::values.vsync_mode.SetGlobal(false);
+ UpdateVsyncSetting();
+ restore_button->setVisible(true);
+ };
+ QObject::connect(widget->combobox, QOverload<int>::of(&QComboBox::activated),
+ [set_non_global]() { set_non_global(); });
+ vsync_restore_global_button = restore_button;
+ }
hold_graphics.emplace(setting->Id(), widget);
} else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
// Keep track of the aspect ratio combobox to update other UI tabs that need it
@@ -400,11 +440,7 @@ void ConfigureGraphics::ApplyConfiguration() {
func(powered_on);
}
- if (Settings::IsConfiguringGlobal()) {
- const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
- const auto vsync_mode = PresentModeToSetting(mode);
- Settings::values.vsync_mode.SetValue(vsync_mode);
- }
+ UpdateVsyncSetting();
Settings::values.vulkan_device.SetGlobal(true);
Settings::values.shader_backend.SetGlobal(true);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 9c24a56db..5c8286836 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -62,7 +62,8 @@ private:
void Setup(const ConfigurationShared::Builder& builder);
- void PopulateVSyncModeSelection();
+ void PopulateVSyncModeSelection(bool use_setting);
+ void UpdateVsyncSetting() const;
void UpdateBackgroundColorButton(QColor color);
void UpdateAPILayout();
void UpdateDeviceSelection(int device);
@@ -104,6 +105,7 @@ private:
QComboBox* api_combobox;
QComboBox* shader_backend_combobox;
QComboBox* vsync_mode_combobox;
+ QPushButton* vsync_restore_global_button;
QWidget* vulkan_device_widget;
QWidget* api_widget;
QWidget* shader_backend_widget;
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index a71000b72..6aca71d7c 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -348,43 +348,45 @@ void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
- player.buttons[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
+ player.buttons[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
- WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
- player.analogs[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
+ player.analogs[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
- WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
- player.motions[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
+ player.motions[i], std::make_optional(default_param));
}
}
void QtConfig::SaveDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
- Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
+ WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
+ Settings::values.debug_pad_buttons[i],
+ std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
- WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
- Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
+ WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
+ Settings::values.debug_pad_analogs[i],
+ std::make_optional(default_param));
}
}
void QtConfig::SaveHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
- WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
- std::make_optional(default_param));
+ WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
+ std::make_optional(default_param));
}
void QtConfig::SaveQtControlValues() {
@@ -409,19 +411,20 @@ void QtConfig::SavePathValues() {
WriteCategory(Settings::Category::Paths);
- WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
+ WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path);
BeginArray(std::string("gamedirs"));
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i);
const auto& game_dir = UISettings::values.game_dirs[i];
- WriteSetting(std::string("path"), game_dir.path);
- WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false));
- WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
+ WriteStringSetting(std::string("path"), game_dir.path);
+ WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
+ std::make_optional(false));
+ WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
}
EndArray();
- WriteSetting(std::string("recentFiles"),
- UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
+ WriteStringSetting(std::string("recentFiles"),
+ UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
EndGroup();
}
@@ -438,14 +441,14 @@ void QtConfig::SaveShortcutValues() {
BeginGroup(group);
BeginGroup(name);
- WriteSetting(std::string("KeySeq"), shortcut.keyseq,
- std::make_optional(default_hotkey.keyseq));
- WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
- std::make_optional(default_hotkey.controller_keyseq));
- WriteSetting(std::string("Context"), shortcut.context,
- std::make_optional(default_hotkey.context));
- WriteSetting(std::string("Repeat"), shortcut.repeat,
- std::make_optional(default_hotkey.repeat));
+ WriteStringSetting(std::string("KeySeq"), shortcut.keyseq,
+ std::make_optional(default_hotkey.keyseq));
+ WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
+ std::make_optional(default_hotkey.controller_keyseq));
+ WriteIntegerSetting(std::string("Context"), shortcut.context,
+ std::make_optional(default_hotkey.context));
+ WriteBooleanSetting(std::string("Repeat"), shortcut.repeat,
+ std::make_optional(default_hotkey.repeat));
EndGroup(); // name
EndGroup(); // group
@@ -460,9 +463,10 @@ void QtConfig::SaveUIValues() {
WriteCategory(Settings::Category::Ui);
WriteCategory(Settings::Category::UiGeneral);
- WriteSetting(std::string("theme"), UISettings::values.theme,
- std::make_optional(std::string(
- UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
+ WriteStringSetting(
+ std::string("theme"), UISettings::values.theme,
+ std::make_optional(std::string(
+ UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
SaveUIGamelistValues();
SaveUILayoutValues();
@@ -482,7 +486,7 @@ void QtConfig::SaveUIGamelistValues() {
BeginArray(std::string("favorites"));
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
SetArrayIndex(i);
- WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
+ WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
}
EndArray(); // favorites
@@ -506,14 +510,15 @@ void QtConfig::SaveMultiplayerValues() {
BeginArray(std::string("username_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
- WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]);
+ WriteStringSetting(std::string("username"),
+ UISettings::values.multiplayer_ban_list.first[i]);
}
EndArray(); // username_ban_list
BeginArray(std::string("ip_ban_list"));
for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
- WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
+ WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
}
EndArray(); // ip_ban_list
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 059fcf041..c789c1e59 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -5342,6 +5342,10 @@ int main(int argc, char* argv[]) {
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
qputenv("DISPLAY", ":0");
}
+
+ // Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
+ // suffix.
+ QGuiApplication::setDesktopFileName(QStringLiteral("org.yuzu_emu.yuzu"));
#endif
SetHighDPIAttributes();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 530e445f9..366e806d5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -168,14 +168,6 @@ class GMainWindow : public QMainWindow {
/// Max number of recently loaded items to keep track of
static const int max_recent_files_item = 10;
- // TODO: Make use of this!
- enum {
- UI_IDLE,
- UI_EMU_BOOTING,
- UI_EMU_RUNNING,
- UI_EMU_STOPPING,
- };
-
enum {
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
CREATE_SHORTCUT_MSGBOX_SUCCESS,
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index a415a953f..74da97e21 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -94,7 +94,7 @@ void DirectConnectWindow::Connect() {
// Store settings
UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
UISettings::values.multiplayer_ip = ui->ip->text().toStdString();
- if (ui->port->isModified() && !ui->port->text().isEmpty()) {
+ if (!ui->port->text().isEmpty()) {
UISettings::values.multiplayer_port = ui->port->text().toInt();
} else {
UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 5153cdb79..1a35d471c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -20,7 +20,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co
: input_subsystem{input_subsystem_}, system{system_} {
input_subsystem->Initialize();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
- LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
+ LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError());
exit(1);
}
SDL_SetMainReady();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 9ed47d453..8b916f05c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -28,7 +28,8 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
SDL_SysWMinfo wm;
SDL_VERSION(&wm.version);
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
- LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
+ LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
+ SDL_GetError());
std::exit(EXIT_FAILURE);
}
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index 39fd8050c..e81bf5d45 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -213,43 +213,45 @@ void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
- player.buttons[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
+ player.buttons[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
- WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
- player.analogs[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
+ player.analogs[i], std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
- WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
- player.motions[i], std::make_optional(default_param));
+ WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
+ player.motions[i], std::make_optional(default_param));
}
}
void SdlConfig::SaveDebugControlValues() {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
- Settings::values.debug_pad_buttons[i], std::make_optional(default_param));
+ WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
+ Settings::values.debug_pad_buttons[i],
+ std::make_optional(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
- WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
- Settings::values.debug_pad_analogs[i], std::make_optional(default_param));
+ WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
+ Settings::values.debug_pad_analogs[i],
+ std::make_optional(default_param));
}
}
void SdlConfig::SaveHidbusValues() {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
- WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
- std::make_optional(default_param));
+ WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
+ std::make_optional(default_param));
}
std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {