diff options
Diffstat (limited to 'src')
223 files changed, 7289 insertions, 3004 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index f763c657e3..53aafa08c2 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 9b08f008d1..93c8ce9225 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 16f06cd0af..86bd336726 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 21e4e1afd5..16fb87614f 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 9551fc05e4..fee80bb216 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 2e97aee2cc..12f7aa1ab2 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 a7e965589f..2ad2f49664 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 0dce8ad8dc..816336820f 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 d7b38f62d0..9efc1705d4 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 b1d3c0040a..b04d1208fa 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 d19f20dc29..5ae05b5cc2 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 a13faf3c78..c87486c901 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 2c28dda886..b14a4f96e4 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 518b1e7837..113bf7c248 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 0000000000..a0eeadf4bc --- /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 0000000000..26cfeb1db5 --- /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 0000000000..6bd74c82fe --- /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 0000000000..d728164e59 --- /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 fc0eeb9ad3..54380323e3 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 b4117d7610..622ae996e3 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 0197fd712f..de0794a17f 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 7e8f058c14..0000000000 --- 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 00c6bf90e7..132f002fbe 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 7512d5eed9..a4c14b3a77 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 0000000000..a233ba25c7 --- /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 f312e24cf1..6f7f40e436 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 52d8ecfeb0..1e884ffdd2 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 ccb0c06f74..8eb803e1b0 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 9c3a5a9b29..08aed3216e 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 2c12874e15..693e1e3f07 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 3733f5a3c5..559ae83eb7 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 e7a86d3fd0..c79ad7d76b 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 24030be421..784d1412f6 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 324d9e9cdc..5359024833 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 5cb84182e0..1c08e2e1b4 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 4885b4f6fb..3637816528 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 e3915ef4f7..0363ff3b6a 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 dc527965c9..1c6f5db93a 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 0b80b04a49..83aa1b7813 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 51e5593215..b77906ed6d 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 c41d9d1eae..ee42ae529d 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 b58a7073fd..8c57d47c6f 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 6026b7dc22..e2c2cade3f 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 0000000000..6832087959 --- /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 0000000000..ee5b0bf43a --- /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 e540375b8f..860c39e6a9 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 747c5850c2..72fbb05af7 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 d4f27197c9..7a267f8c06 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 12e5e2498e..2a9926e9e9 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 5c961b202f..e7e9fdb38a 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 ea52bbfa67..07709d4e5c 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 c82e174959..1a290ad770 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 96ab39cb88..e2ec2164c7 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 698c9c8add..5dc7e5d59d 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 806c7c9e9f..495963eefd 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 af1c34bc36..854509463b 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 c542633dbc..3cd6713654 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 0000000000..e6e9fc45be --- /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 eef7c31160..53dd188151 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 f34865e263..36478f7224 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 185ac7cbf8..b580efe615 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 dff14756e6..c811c8ad56 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 4f3dd026f8..08cd982b30 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 1311e66a90..123b3da7ec 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 8e81c66a55..def888d153 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 8fb0329120..6a2be3d9bc 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 b14f749764..66f444d393 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 473204db76..ba5add0dcb 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 d6b5abc68e..fc536413b7 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 21548f0a9b..7e4dff7f3d 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 7be1322cc1..31033634c8 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 763a44feec..539c7f7afb 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 76ee97d782..a53092b87e 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 4bf285f361..a81ed6af03 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 78d43d729c..48889253dc 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 636b3f9936..7bea1a1c20 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 badd75d2a0..770743d21b 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 274fee493f..d2288c30d1 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 11b1b977ef..68cea978a0 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 7633a51fb2..94ea3527ab 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 d7660630c0..1bf68e6b04 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 8e2e40307d..4e947dd6bc 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 4232891450..8c1549559e 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 556d230b3e..077cafc969 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 3a2635e1ff..068e71dffd 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 4b114e39b8..53c0e33162 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 e33a88e24a..adaabdd6d6 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 4038919194..2876c231b2 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 44d7a8f028..4a1f6027ee 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 7d9a6e9cf0..7d3421929d 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 e9925d231d..f13e232b24 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 8a0b08761c..530b452182 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 e479dacde1..1030f0c124 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 78c88902cc..5d41021458 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 75b275310e..d528a9bb38 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 ada9987722..231e4d0e1b 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 47a3e7bb01..85cc4f561e 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 e1ad78607e..38e71d516d 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 a266d7c214..97eb56ff0b 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 23e56c77a6..bd4ca753b4 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 6a7bf94162..91f33aabdb 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 fe2ed8df87..31da86074d 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 c8e74c7643..b4ff663c24 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 e7991f93a0..0862fdc2f7 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 0000000000..7847c080e8 --- /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 d2052fb170..dcc4715c5c 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 3961d2b5ff..4b574c2e58 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 3d1c9ce23d..e3351f83c7 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 0bcd870623..afca7154c6 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 4326c78219..b34b85ece8 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 0000000000..ceeb78d36d --- /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 0000000000..ec939fa9f2 --- /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 7d2370b4fa..dc83f90f3b 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 8ab29eca84..dd00b24023 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 0000000000..d5514c9653 --- /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 0000000000..d81f814c3e --- /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 f658005f60..c73da13ee1 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 41fdfcd036..78da1552a2 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 0000000000..1397379f39 --- /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 0000000000..e91c2aa5d2 --- /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 871e5036a1..c069bcbb25 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 4d72171b9e..e8ca326c6f 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 de5b2c804f..3a8d1751b5 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 363f316a56..cefad956ca 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 53a737cf54..17cd0d7a07 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 4e24123564..8ab3330648 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 0000000000..d2423b6d3d --- /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 0000000000..f799a9f9c6 --- /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 0000000000..0a9341a39d --- /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 0000000000..4c7e6ab0e2 --- /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 51581188ec..0bc5169c69 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 36b72f9ea2..adab609111 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 0000000000..d44b1f4cc5 --- /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 0000000000..59964bf636 --- /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 e2a5f5d790..0000000000 --- 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 469750006a..b585a5829d 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 5b6305bfcd..945d359bee 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 a5ce2562bd..419c33a8cb 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 2986c113e0..976043b9c9 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 0000000000..6c543031dc --- /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 0000000000..9663682644 --- /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 6dc976fe18..bb14aa61ec 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 afbcb019f7..bd28731817 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 de24b0401c..a953c92b32 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 5cc88c4a17..4823de7433 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 b87cc10e30..6a2ed287a1 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 80aac221b8..ffa7e144da 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 c29b5e8823..85a1df1334 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 39b9a4474b..05ed31273d 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 6c6cbd8027..1f41e645dd 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 5ad7cb5649..7a21d8eb8a 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 38955932cd..39df77e430 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 18d464c63b..40d86943e0 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 0e222362e5..4b02872fba 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 65851fc058..77aa6d7d13 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 0000000000..2b480488a8 --- /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 0000000000..6376d4c4eb --- /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 6352b09a90..aa8aaa2d97 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 17110d3f1d..f0658bb5db 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 6808247a9a..15edb23e03 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 00531b0215..39124c5fdc 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 0653779d59..8e637f9637 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 3785d93d88..853f76fced 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 296ee6e894..1095dcf6c3 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 ff74f588a9..4ae32a9c10 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 7dce28fe01..7f0fb91d04 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 60ee78e89a..c9f8707b75 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 169bf4c8cc..8176a41be8 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 c1879e78f9..dddfaf4a4c 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 3fc4024dc7..7bc5b5ae50 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 284abdd287..ced2168d13 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 98ebbbf329..9d42c726e4 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 0d6df5217e..2efbc11f38 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 d9f99148bc..20de91ff44 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 b3812af17f..b016316499 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 e5a78a9140..feca5105f9 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 22ceca19c0..800754554e 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 eb3cc23cc6..0442adc831 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 1aa79863de..56019ad891 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 15285ab0ac..e30bf094aa 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 70292686fc..cb82a326c1 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 ec12c843a3..e4a73a360a 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 d374c976a9..100437f0e5 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 66901a9654..7578d41cc0 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 b4b4afd37d..ed13e68209 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 1a28e862b0..cb040c942d 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 f08afbf9ab..81898a1d38 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 592c28ba3a..95ba4f76cc 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 422d4d859a..56fbff3066 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 046c8085e5..46e853e040 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 9fcaeeac7f..a64404ce4d 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 af1469147b..49224ca85c 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 b787b69948..517ac14dd5 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 1e8708f59e..2c18de1669 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 ec142d48ea..fef7360edb 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 4832c03c57..7a5fad7350 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(¶ms); PrepareDraw(params.is_indexed, [this, ¶ms](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 ceffe1f1ea..ce3460938d 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 eae8fd1108..1d2c9b70a6 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 77362acd26..6ca8227bdb 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 26f2d0ea72..30df41b7de 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 5958f52f76..3c61799faf 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 0b3fbd6d05..dc300d7cb4 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 2a13b2a72e..d1841198da 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 5e7518d968..792ed9615a 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 078777cdda..95954ade7f 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 59829c88bd..241fc34bec 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 4edbe57003..492440ac48 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 b90f3d44e2..6b372e3365 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 3e26474a3e..a018c6df46 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 41d1ec0a3a..4e3b9b8ace 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 15596c9250..fcf70068ec 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 1fda0042d7..727bbd98d2 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 4f3846345c..701817086f 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 70cf14afa9..074aed9645 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 fd6bebf0f3..0836bcb7e7 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 9c24a56db2..5c8286836a 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 a71000b72f..6aca71d7c4 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 059fcf0413..c789c1e59e 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 530e445f92..366e806d5b 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 a415a953f6..74da97e218 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 5153cdb792..1a35d471c1 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 9ed47d453a..8b916f05c8 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 39fd8050c6..e81bf5d45f 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) { |