diff options
Diffstat (limited to 'src')
54 files changed, 1439 insertions, 357 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 51d949d65..742685fb0 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -54,6 +54,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 54 | <activity | 54 | <activity |
| 55 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | 55 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |
| 56 | android:theme="@style/Theme.Yuzu.Main" | 56 | android:theme="@style/Theme.Yuzu.Main" |
| 57 | android:launchMode="singleTop" | ||
| 57 | android:screenOrientation="userLandscape" | 58 | android:screenOrientation="userLandscape" |
| 58 | android:supportsPictureInPicture="true" | 59 | android:supportsPictureInPicture="true" |
| 59 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" | 60 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" |
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 ae665ed2e..7461fb093 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 | |||
| @@ -34,11 +34,14 @@ import androidx.core.view.WindowCompat | |||
| 34 | import androidx.core.view.WindowInsetsCompat | 34 | import androidx.core.view.WindowInsetsCompat |
| 35 | import androidx.core.view.WindowInsetsControllerCompat | 35 | import androidx.core.view.WindowInsetsControllerCompat |
| 36 | import androidx.navigation.fragment.NavHostFragment | 36 | import androidx.navigation.fragment.NavHostFragment |
| 37 | import androidx.preference.PreferenceManager | ||
| 37 | import org.yuzu.yuzu_emu.NativeLibrary | 38 | import org.yuzu.yuzu_emu.NativeLibrary |
| 38 | import org.yuzu.yuzu_emu.R | 39 | import org.yuzu.yuzu_emu.R |
| 40 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 39 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding | 41 | import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding |
| 40 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting | 42 | import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
| 41 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 43 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 44 | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||
| 42 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel | 45 | import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel |
| 43 | import org.yuzu.yuzu_emu.model.Game | 46 | import org.yuzu.yuzu_emu.model.Game |
| 44 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper | 47 | import org.yuzu.yuzu_emu.utils.ControllerMappingHelper |
| @@ -47,6 +50,7 @@ import org.yuzu.yuzu_emu.utils.InputHandler | |||
| 47 | import org.yuzu.yuzu_emu.utils.MemoryUtil | 50 | import org.yuzu.yuzu_emu.utils.MemoryUtil |
| 48 | import org.yuzu.yuzu_emu.utils.NfcReader | 51 | import org.yuzu.yuzu_emu.utils.NfcReader |
| 49 | import org.yuzu.yuzu_emu.utils.ThemeHelper | 52 | import org.yuzu.yuzu_emu.utils.ThemeHelper |
| 53 | import java.text.NumberFormat | ||
| 50 | import kotlin.math.roundToInt | 54 | import kotlin.math.roundToInt |
| 51 | 55 | ||
| 52 | class EmulationActivity : AppCompatActivity(), SensorEventListener { | 56 | class EmulationActivity : AppCompatActivity(), SensorEventListener { |
| @@ -106,17 +110,26 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 106 | inputHandler = InputHandler() | 110 | inputHandler = InputHandler() |
| 107 | inputHandler.initialize() | 111 | inputHandler.initialize() |
| 108 | 112 | ||
| 109 | val memoryUtil = MemoryUtil(this) | 113 | val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 110 | if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { | 114 | if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { |
| 111 | Toast.makeText( | 115 | if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { |
| 112 | this, | 116 | Toast.makeText( |
| 113 | getString( | 117 | this, |
| 114 | R.string.device_memory_inadequate, | 118 | getString( |
| 115 | memoryUtil.getDeviceRAM(), | 119 | R.string.device_memory_inadequate, |
| 116 | "8 ${getString(R.string.memory_gigabyte)}" | 120 | MemoryUtil.getDeviceRAM(), |
| 117 | ), | 121 | getString( |
| 118 | Toast.LENGTH_LONG | 122 | R.string.memory_formatted, |
| 119 | ).show() | 123 | NumberFormat.getInstance().format(MemoryUtil.REQUIRED_MEMORY), |
| 124 | getString(R.string.memory_gigabyte) | ||
| 125 | ) | ||
| 126 | ), | ||
| 127 | Toast.LENGTH_LONG | ||
| 128 | ).show() | ||
| 129 | preferences.edit() | ||
| 130 | .putBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, true) | ||
| 131 | .apply() | ||
| 132 | } | ||
| 120 | } | 133 | } |
| 121 | 134 | ||
| 122 | // Start a foreground service to prevent the app from getting killed in the background | 135 | // Start a foreground service to prevent the app from getting killed in the background |
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 88afb2223..a6251bafd 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 | |||
| @@ -110,25 +110,38 @@ class Settings { | |||
| 110 | const val SECTION_THEME = "Theme" | 110 | const val SECTION_THEME = "Theme" |
| 111 | const val SECTION_DEBUG = "Debug" | 111 | const val SECTION_DEBUG = "Debug" |
| 112 | 112 | ||
| 113 | const val PREF_OVERLAY_INIT = "OverlayInit" | 113 | const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" |
| 114 | |||
| 115 | const val PREF_OVERLAY_VERSION = "OverlayVersion" | ||
| 116 | const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" | ||
| 117 | const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" | ||
| 118 | const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" | ||
| 119 | val overlayLayoutPrefs = listOf( | ||
| 120 | PREF_LANDSCAPE_OVERLAY_VERSION, | ||
| 121 | PREF_PORTRAIT_OVERLAY_VERSION, | ||
| 122 | PREF_FOLDABLE_OVERLAY_VERSION | ||
| 123 | ) | ||
| 124 | |||
| 114 | const val PREF_CONTROL_SCALE = "controlScale" | 125 | const val PREF_CONTROL_SCALE = "controlScale" |
| 115 | const val PREF_CONTROL_OPACITY = "controlOpacity" | 126 | const val PREF_CONTROL_OPACITY = "controlOpacity" |
| 116 | const val PREF_TOUCH_ENABLED = "isTouchEnabled" | 127 | const val PREF_TOUCH_ENABLED = "isTouchEnabled" |
| 117 | const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0" | 128 | const val PREF_BUTTON_A = "buttonToggle0" |
| 118 | const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1" | 129 | const val PREF_BUTTON_B = "buttonToggle1" |
| 119 | const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2" | 130 | const val PREF_BUTTON_X = "buttonToggle2" |
| 120 | const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3" | 131 | const val PREF_BUTTON_Y = "buttonToggle3" |
| 121 | const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4" | 132 | const val PREF_BUTTON_L = "buttonToggle4" |
| 122 | const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5" | 133 | const val PREF_BUTTON_R = "buttonToggle5" |
| 123 | const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6" | 134 | const val PREF_BUTTON_ZL = "buttonToggle6" |
| 124 | const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7" | 135 | const val PREF_BUTTON_ZR = "buttonToggle7" |
| 125 | const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8" | 136 | const val PREF_BUTTON_PLUS = "buttonToggle8" |
| 126 | const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9" | 137 | const val PREF_BUTTON_MINUS = "buttonToggle9" |
| 127 | const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10" | 138 | const val PREF_BUTTON_DPAD = "buttonToggle10" |
| 128 | const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11" | 139 | const val PREF_STICK_L = "buttonToggle11" |
| 129 | const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12" | 140 | const val PREF_STICK_R = "buttonToggle12" |
| 130 | const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" | 141 | const val PREF_BUTTON_STICK_L = "buttonToggle13" |
| 131 | const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" | 142 | const val PREF_BUTTON_STICK_R = "buttonToggle14" |
| 143 | const val PREF_BUTTON_HOME = "buttonToggle15" | ||
| 144 | const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" | ||
| 132 | 145 | ||
| 133 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" | 146 | const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" |
| 134 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" | 147 | const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" |
| @@ -143,6 +156,30 @@ class Settings { | |||
| 143 | 156 | ||
| 144 | private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() | 157 | private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() |
| 145 | 158 | ||
| 159 | val overlayPreferences = listOf( | ||
| 160 | PREF_OVERLAY_VERSION, | ||
| 161 | PREF_CONTROL_SCALE, | ||
| 162 | PREF_CONTROL_OPACITY, | ||
| 163 | PREF_TOUCH_ENABLED, | ||
| 164 | PREF_BUTTON_A, | ||
| 165 | PREF_BUTTON_B, | ||
| 166 | PREF_BUTTON_X, | ||
| 167 | PREF_BUTTON_Y, | ||
| 168 | PREF_BUTTON_L, | ||
| 169 | PREF_BUTTON_R, | ||
| 170 | PREF_BUTTON_ZL, | ||
| 171 | PREF_BUTTON_ZR, | ||
| 172 | PREF_BUTTON_PLUS, | ||
| 173 | PREF_BUTTON_MINUS, | ||
| 174 | PREF_BUTTON_DPAD, | ||
| 175 | PREF_STICK_L, | ||
| 176 | PREF_STICK_R, | ||
| 177 | PREF_BUTTON_HOME, | ||
| 178 | PREF_BUTTON_SCREENSHOT, | ||
| 179 | PREF_BUTTON_STICK_L, | ||
| 180 | PREF_BUTTON_STICK_R | ||
| 181 | ) | ||
| 182 | |||
| 146 | const val LayoutOption_Unspecified = 0 | 183 | const val LayoutOption_Unspecified = 0 |
| 147 | const val LayoutOption_MobilePortrait = 4 | 184 | const val LayoutOption_MobilePortrait = 4 |
| 148 | const val LayoutOption_MobileLandscape = 5 | 185 | const val LayoutOption_MobileLandscape = 5 |
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 09976db62..0e7c1ba88 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 | |||
| @@ -212,9 +212,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 212 | } | 212 | } |
| 213 | if (!isInFoldableLayout) { | 213 | if (!isInFoldableLayout) { |
| 214 | if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { | 214 | if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { |
| 215 | binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT | 215 | binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT |
| 216 | } else { | 216 | } else { |
| 217 | binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE | 217 | binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE |
| 218 | } | 218 | } |
| 219 | } | 219 | } |
| 220 | if (!binding.surfaceInputOverlay.isInEditMode) { | 220 | if (!binding.surfaceInputOverlay.isInEditMode) { |
| @@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 260 | .remove(Settings.PREF_CONTROL_SCALE) | 260 | .remove(Settings.PREF_CONTROL_SCALE) |
| 261 | .remove(Settings.PREF_CONTROL_OPACITY) | 261 | .remove(Settings.PREF_CONTROL_OPACITY) |
| 262 | .apply() | 262 | .apply() |
| 263 | binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() } | 263 | binding.surfaceInputOverlay.post { |
| 264 | binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() | ||
| 265 | } | ||
| 264 | } | 266 | } |
| 265 | 267 | ||
| 266 | private fun updateShowFpsOverlay() { | 268 | private fun updateShowFpsOverlay() { |
| @@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 337 | binding.inGameMenu.layoutParams.height = it.bounds.bottom | 339 | binding.inGameMenu.layoutParams.height = it.bounds.bottom |
| 338 | 340 | ||
| 339 | isInFoldableLayout = true | 341 | isInFoldableLayout = true |
| 340 | binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE | 342 | binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE |
| 341 | refreshInputOverlay() | 343 | refreshInputOverlay() |
| 342 | } | 344 | } |
| 343 | } | 345 | } |
| @@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 410 | R.id.menu_toggle_controls -> { | 412 | R.id.menu_toggle_controls -> { |
| 411 | val preferences = | 413 | val preferences = |
| 412 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 414 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 413 | val optionsArray = BooleanArray(15) | 415 | val optionsArray = BooleanArray(Settings.overlayPreferences.size) |
| 414 | for (i in 0..14) { | 416 | Settings.overlayPreferences.forEachIndexed { i, _ -> |
| 415 | optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13) | 417 | optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15) |
| 416 | } | 418 | } |
| 417 | 419 | ||
| 418 | val dialog = MaterialAlertDialogBuilder(requireContext()) | 420 | val dialog = MaterialAlertDialogBuilder(requireContext()) |
| @@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 436 | dialog.getButton(AlertDialog.BUTTON_NEUTRAL) | 438 | dialog.getButton(AlertDialog.BUTTON_NEUTRAL) |
| 437 | .setOnClickListener { | 439 | .setOnClickListener { |
| 438 | val isChecked = !optionsArray[0] | 440 | val isChecked = !optionsArray[0] |
| 439 | for (i in 0..14) { | 441 | Settings.overlayPreferences.forEachIndexed { i, _ -> |
| 440 | optionsArray[i] = isChecked | 442 | optionsArray[i] = isChecked |
| 441 | dialog.listView.setItemChecked(i, isChecked) | 443 | dialog.listView.setItemChecked(i, isChecked) |
| 442 | preferences.edit() | 444 | preferences.edit() |
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 6251ec783..c055c2e35 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 | |||
| @@ -51,15 +51,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 51 | 51 | ||
| 52 | private lateinit var windowInsets: WindowInsets | 52 | private lateinit var windowInsets: WindowInsets |
| 53 | 53 | ||
| 54 | var orientation = LANDSCAPE | 54 | var layout = LANDSCAPE |
| 55 | 55 | ||
| 56 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { | 56 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { |
| 57 | super.onLayout(changed, left, top, right, bottom) | 57 | super.onLayout(changed, left, top, right, bottom) |
| 58 | 58 | ||
| 59 | windowInsets = rootWindowInsets | 59 | windowInsets = rootWindowInsets |
| 60 | 60 | ||
| 61 | if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { | 61 | val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0) |
| 62 | defaultOverlay() | 62 | if (overlayVersion != OVERLAY_VERSION) { |
| 63 | resetAllLayouts() | ||
| 64 | } else { | ||
| 65 | val layoutIndex = overlayLayouts.indexOf(layout) | ||
| 66 | val currentLayoutVersion = | ||
| 67 | preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0) | ||
| 68 | if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) { | ||
| 69 | resetCurrentLayout() | ||
| 70 | } | ||
| 63 | } | 71 | } |
| 64 | 72 | ||
| 65 | // Load the controls. | 73 | // Load the controls. |
| @@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 266 | MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { | 274 | MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { |
| 267 | // Persist button position by saving new place. | 275 | // Persist button position by saving new place. |
| 268 | saveControlPosition( | 276 | saveControlPosition( |
| 269 | buttonBeingConfigured!!.buttonId, | 277 | buttonBeingConfigured!!.prefId, |
| 270 | buttonBeingConfigured!!.bounds.centerX(), | 278 | buttonBeingConfigured!!.bounds.centerX(), |
| 271 | buttonBeingConfigured!!.bounds.centerY(), | 279 | buttonBeingConfigured!!.bounds.centerY(), |
| 272 | orientation | 280 | layout |
| 273 | ) | 281 | ) |
| 274 | buttonBeingConfigured = null | 282 | buttonBeingConfigured = null |
| 275 | } | 283 | } |
| @@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 299 | MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { | 307 | MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { |
| 300 | // Persist button position by saving new place. | 308 | // Persist button position by saving new place. |
| 301 | saveControlPosition( | 309 | saveControlPosition( |
| 302 | dpadBeingConfigured!!.upId, | 310 | Settings.PREF_BUTTON_DPAD, |
| 303 | dpadBeingConfigured!!.bounds.centerX(), | 311 | dpadBeingConfigured!!.bounds.centerX(), |
| 304 | dpadBeingConfigured!!.bounds.centerY(), | 312 | dpadBeingConfigured!!.bounds.centerY(), |
| 305 | orientation | 313 | layout |
| 306 | ) | 314 | ) |
| 307 | dpadBeingConfigured = null | 315 | dpadBeingConfigured = null |
| 308 | } | 316 | } |
| @@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 330 | MotionEvent.ACTION_UP, | 338 | MotionEvent.ACTION_UP, |
| 331 | MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { | 339 | MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { |
| 332 | saveControlPosition( | 340 | saveControlPosition( |
| 333 | joystickBeingConfigured!!.buttonId, | 341 | joystickBeingConfigured!!.prefId, |
| 334 | joystickBeingConfigured!!.bounds.centerX(), | 342 | joystickBeingConfigured!!.bounds.centerX(), |
| 335 | joystickBeingConfigured!!.bounds.centerY(), | 343 | joystickBeingConfigured!!.bounds.centerY(), |
| 336 | orientation | 344 | layout |
| 337 | ) | 345 | ) |
| 338 | joystickBeingConfigured = null | 346 | joystickBeingConfigured = null |
| 339 | } | 347 | } |
| @@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 343 | return true | 351 | return true |
| 344 | } | 352 | } |
| 345 | 353 | ||
| 346 | private fun addOverlayControls(orientation: String) { | 354 | private fun addOverlayControls(layout: String) { |
| 347 | val windowSize = getSafeScreenSize(context) | 355 | val windowSize = getSafeScreenSize(context) |
| 348 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { | 356 | if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) { |
| 349 | overlayButtons.add( | 357 | overlayButtons.add( |
| 350 | initializeOverlayButton( | 358 | initializeOverlayButton( |
| 351 | context, | 359 | context, |
| @@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 353 | R.drawable.facebutton_a, | 361 | R.drawable.facebutton_a, |
| 354 | R.drawable.facebutton_a_depressed, | 362 | R.drawable.facebutton_a_depressed, |
| 355 | ButtonType.BUTTON_A, | 363 | ButtonType.BUTTON_A, |
| 356 | orientation | 364 | Settings.PREF_BUTTON_A, |
| 365 | layout | ||
| 357 | ) | 366 | ) |
| 358 | ) | 367 | ) |
| 359 | } | 368 | } |
| 360 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) { | 369 | if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) { |
| 361 | overlayButtons.add( | 370 | overlayButtons.add( |
| 362 | initializeOverlayButton( | 371 | initializeOverlayButton( |
| 363 | context, | 372 | context, |
| @@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 365 | R.drawable.facebutton_b, | 374 | R.drawable.facebutton_b, |
| 366 | R.drawable.facebutton_b_depressed, | 375 | R.drawable.facebutton_b_depressed, |
| 367 | ButtonType.BUTTON_B, | 376 | ButtonType.BUTTON_B, |
| 368 | orientation | 377 | Settings.PREF_BUTTON_B, |
| 378 | layout | ||
| 369 | ) | 379 | ) |
| 370 | ) | 380 | ) |
| 371 | } | 381 | } |
| 372 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) { | 382 | if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) { |
| 373 | overlayButtons.add( | 383 | overlayButtons.add( |
| 374 | initializeOverlayButton( | 384 | initializeOverlayButton( |
| 375 | context, | 385 | context, |
| @@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 377 | R.drawable.facebutton_x, | 387 | R.drawable.facebutton_x, |
| 378 | R.drawable.facebutton_x_depressed, | 388 | R.drawable.facebutton_x_depressed, |
| 379 | ButtonType.BUTTON_X, | 389 | ButtonType.BUTTON_X, |
| 380 | orientation | 390 | Settings.PREF_BUTTON_X, |
| 391 | layout | ||
| 381 | ) | 392 | ) |
| 382 | ) | 393 | ) |
| 383 | } | 394 | } |
| 384 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) { | 395 | if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) { |
| 385 | overlayButtons.add( | 396 | overlayButtons.add( |
| 386 | initializeOverlayButton( | 397 | initializeOverlayButton( |
| 387 | context, | 398 | context, |
| @@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 389 | R.drawable.facebutton_y, | 400 | R.drawable.facebutton_y, |
| 390 | R.drawable.facebutton_y_depressed, | 401 | R.drawable.facebutton_y_depressed, |
| 391 | ButtonType.BUTTON_Y, | 402 | ButtonType.BUTTON_Y, |
| 392 | orientation | 403 | Settings.PREF_BUTTON_Y, |
| 404 | layout | ||
| 393 | ) | 405 | ) |
| 394 | ) | 406 | ) |
| 395 | } | 407 | } |
| 396 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) { | 408 | if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) { |
| 397 | overlayButtons.add( | 409 | overlayButtons.add( |
| 398 | initializeOverlayButton( | 410 | initializeOverlayButton( |
| 399 | context, | 411 | context, |
| @@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 401 | R.drawable.l_shoulder, | 413 | R.drawable.l_shoulder, |
| 402 | R.drawable.l_shoulder_depressed, | 414 | R.drawable.l_shoulder_depressed, |
| 403 | ButtonType.TRIGGER_L, | 415 | ButtonType.TRIGGER_L, |
| 404 | orientation | 416 | Settings.PREF_BUTTON_L, |
| 417 | layout | ||
| 405 | ) | 418 | ) |
| 406 | ) | 419 | ) |
| 407 | } | 420 | } |
| 408 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) { | 421 | if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) { |
| 409 | overlayButtons.add( | 422 | overlayButtons.add( |
| 410 | initializeOverlayButton( | 423 | initializeOverlayButton( |
| 411 | context, | 424 | context, |
| @@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 413 | R.drawable.r_shoulder, | 426 | R.drawable.r_shoulder, |
| 414 | R.drawable.r_shoulder_depressed, | 427 | R.drawable.r_shoulder_depressed, |
| 415 | ButtonType.TRIGGER_R, | 428 | ButtonType.TRIGGER_R, |
| 416 | orientation | 429 | Settings.PREF_BUTTON_R, |
| 430 | layout | ||
| 417 | ) | 431 | ) |
| 418 | ) | 432 | ) |
| 419 | } | 433 | } |
| 420 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) { | 434 | if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) { |
| 421 | overlayButtons.add( | 435 | overlayButtons.add( |
| 422 | initializeOverlayButton( | 436 | initializeOverlayButton( |
| 423 | context, | 437 | context, |
| @@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 425 | R.drawable.zl_trigger, | 439 | R.drawable.zl_trigger, |
| 426 | R.drawable.zl_trigger_depressed, | 440 | R.drawable.zl_trigger_depressed, |
| 427 | ButtonType.TRIGGER_ZL, | 441 | ButtonType.TRIGGER_ZL, |
| 428 | orientation | 442 | Settings.PREF_BUTTON_ZL, |
| 443 | layout | ||
| 429 | ) | 444 | ) |
| 430 | ) | 445 | ) |
| 431 | } | 446 | } |
| 432 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) { | 447 | if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) { |
| 433 | overlayButtons.add( | 448 | overlayButtons.add( |
| 434 | initializeOverlayButton( | 449 | initializeOverlayButton( |
| 435 | context, | 450 | context, |
| @@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 437 | R.drawable.zr_trigger, | 452 | R.drawable.zr_trigger, |
| 438 | R.drawable.zr_trigger_depressed, | 453 | R.drawable.zr_trigger_depressed, |
| 439 | ButtonType.TRIGGER_ZR, | 454 | ButtonType.TRIGGER_ZR, |
| 440 | orientation | 455 | Settings.PREF_BUTTON_ZR, |
| 456 | layout | ||
| 441 | ) | 457 | ) |
| 442 | ) | 458 | ) |
| 443 | } | 459 | } |
| 444 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) { | 460 | if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) { |
| 445 | overlayButtons.add( | 461 | overlayButtons.add( |
| 446 | initializeOverlayButton( | 462 | initializeOverlayButton( |
| 447 | context, | 463 | context, |
| @@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 449 | R.drawable.facebutton_plus, | 465 | R.drawable.facebutton_plus, |
| 450 | R.drawable.facebutton_plus_depressed, | 466 | R.drawable.facebutton_plus_depressed, |
| 451 | ButtonType.BUTTON_PLUS, | 467 | ButtonType.BUTTON_PLUS, |
| 452 | orientation | 468 | Settings.PREF_BUTTON_PLUS, |
| 469 | layout | ||
| 453 | ) | 470 | ) |
| 454 | ) | 471 | ) |
| 455 | } | 472 | } |
| 456 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) { | 473 | if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) { |
| 457 | overlayButtons.add( | 474 | overlayButtons.add( |
| 458 | initializeOverlayButton( | 475 | initializeOverlayButton( |
| 459 | context, | 476 | context, |
| @@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 461 | R.drawable.facebutton_minus, | 478 | R.drawable.facebutton_minus, |
| 462 | R.drawable.facebutton_minus_depressed, | 479 | R.drawable.facebutton_minus_depressed, |
| 463 | ButtonType.BUTTON_MINUS, | 480 | ButtonType.BUTTON_MINUS, |
| 464 | orientation | 481 | Settings.PREF_BUTTON_MINUS, |
| 482 | layout | ||
| 465 | ) | 483 | ) |
| 466 | ) | 484 | ) |
| 467 | } | 485 | } |
| 468 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) { | 486 | if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) { |
| 469 | overlayDpads.add( | 487 | overlayDpads.add( |
| 470 | initializeOverlayDpad( | 488 | initializeOverlayDpad( |
| 471 | context, | 489 | context, |
| @@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 473 | R.drawable.dpad_standard, | 491 | R.drawable.dpad_standard, |
| 474 | R.drawable.dpad_standard_cardinal_depressed, | 492 | R.drawable.dpad_standard_cardinal_depressed, |
| 475 | R.drawable.dpad_standard_diagonal_depressed, | 493 | R.drawable.dpad_standard_diagonal_depressed, |
| 476 | orientation | 494 | layout |
| 477 | ) | 495 | ) |
| 478 | ) | 496 | ) |
| 479 | } | 497 | } |
| 480 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) { | 498 | if (preferences.getBoolean(Settings.PREF_STICK_L, true)) { |
| 481 | overlayJoysticks.add( | 499 | overlayJoysticks.add( |
| 482 | initializeOverlayJoystick( | 500 | initializeOverlayJoystick( |
| 483 | context, | 501 | context, |
| @@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 487 | R.drawable.joystick_depressed, | 505 | R.drawable.joystick_depressed, |
| 488 | StickType.STICK_L, | 506 | StickType.STICK_L, |
| 489 | ButtonType.STICK_L, | 507 | ButtonType.STICK_L, |
| 490 | orientation | 508 | Settings.PREF_STICK_L, |
| 509 | layout | ||
| 491 | ) | 510 | ) |
| 492 | ) | 511 | ) |
| 493 | } | 512 | } |
| 494 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) { | 513 | if (preferences.getBoolean(Settings.PREF_STICK_R, true)) { |
| 495 | overlayJoysticks.add( | 514 | overlayJoysticks.add( |
| 496 | initializeOverlayJoystick( | 515 | initializeOverlayJoystick( |
| 497 | context, | 516 | context, |
| @@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 501 | R.drawable.joystick_depressed, | 520 | R.drawable.joystick_depressed, |
| 502 | StickType.STICK_R, | 521 | StickType.STICK_R, |
| 503 | ButtonType.STICK_R, | 522 | ButtonType.STICK_R, |
| 504 | orientation | 523 | Settings.PREF_STICK_R, |
| 524 | layout | ||
| 505 | ) | 525 | ) |
| 506 | ) | 526 | ) |
| 507 | } | 527 | } |
| 508 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) { | 528 | if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) { |
| 509 | overlayButtons.add( | 529 | overlayButtons.add( |
| 510 | initializeOverlayButton( | 530 | initializeOverlayButton( |
| 511 | context, | 531 | context, |
| @@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 513 | R.drawable.facebutton_home, | 533 | R.drawable.facebutton_home, |
| 514 | R.drawable.facebutton_home_depressed, | 534 | R.drawable.facebutton_home_depressed, |
| 515 | ButtonType.BUTTON_HOME, | 535 | ButtonType.BUTTON_HOME, |
| 516 | orientation | 536 | Settings.PREF_BUTTON_HOME, |
| 537 | layout | ||
| 517 | ) | 538 | ) |
| 518 | ) | 539 | ) |
| 519 | } | 540 | } |
| 520 | if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) { | 541 | if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) { |
| 521 | overlayButtons.add( | 542 | overlayButtons.add( |
| 522 | initializeOverlayButton( | 543 | initializeOverlayButton( |
| 523 | context, | 544 | context, |
| @@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 525 | R.drawable.facebutton_screenshot, | 546 | R.drawable.facebutton_screenshot, |
| 526 | R.drawable.facebutton_screenshot_depressed, | 547 | R.drawable.facebutton_screenshot_depressed, |
| 527 | ButtonType.BUTTON_CAPTURE, | 548 | ButtonType.BUTTON_CAPTURE, |
| 528 | orientation | 549 | Settings.PREF_BUTTON_SCREENSHOT, |
| 550 | layout | ||
| 551 | ) | ||
| 552 | ) | ||
| 553 | } | ||
| 554 | if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) { | ||
| 555 | overlayButtons.add( | ||
| 556 | initializeOverlayButton( | ||
| 557 | context, | ||
| 558 | windowSize, | ||
| 559 | R.drawable.button_l3, | ||
| 560 | R.drawable.button_l3_depressed, | ||
| 561 | ButtonType.STICK_L, | ||
| 562 | Settings.PREF_BUTTON_STICK_L, | ||
| 563 | layout | ||
| 564 | ) | ||
| 565 | ) | ||
| 566 | } | ||
| 567 | if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) { | ||
| 568 | overlayButtons.add( | ||
| 569 | initializeOverlayButton( | ||
| 570 | context, | ||
| 571 | windowSize, | ||
| 572 | R.drawable.button_r3, | ||
| 573 | R.drawable.button_r3_depressed, | ||
| 574 | ButtonType.STICK_R, | ||
| 575 | Settings.PREF_BUTTON_STICK_R, | ||
| 576 | layout | ||
| 529 | ) | 577 | ) |
| 530 | ) | 578 | ) |
| 531 | } | 579 | } |
| @@ -539,18 +587,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 539 | 587 | ||
| 540 | // Add all the enabled overlay items back to the HashSet. | 588 | // Add all the enabled overlay items back to the HashSet. |
| 541 | if (EmulationMenuSettings.showOverlay) { | 589 | if (EmulationMenuSettings.showOverlay) { |
| 542 | addOverlayControls(orientation) | 590 | addOverlayControls(layout) |
| 543 | } | 591 | } |
| 544 | invalidate() | 592 | invalidate() |
| 545 | } | 593 | } |
| 546 | 594 | ||
| 547 | private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) { | 595 | private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) { |
| 548 | val windowSize = getSafeScreenSize(context) | 596 | val windowSize = getSafeScreenSize(context) |
| 549 | val min = windowSize.first | 597 | val min = windowSize.first |
| 550 | val max = windowSize.second | 598 | val max = windowSize.second |
| 551 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() | 599 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() |
| 552 | .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) | 600 | .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x) |
| 553 | .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) | 601 | .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y) |
| 554 | .apply() | 602 | .apply() |
| 555 | } | 603 | } |
| 556 | 604 | ||
| @@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 558 | inEditMode = editMode | 606 | inEditMode = editMode |
| 559 | } | 607 | } |
| 560 | 608 | ||
| 561 | private fun defaultOverlay() { | 609 | private fun resetCurrentLayout() { |
| 562 | if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { | 610 | defaultOverlayByLayout(layout) |
| 563 | defaultOverlayByLayout(orientation) | 611 | val layoutIndex = overlayLayouts.indexOf(layout) |
| 564 | } | ||
| 565 | |||
| 566 | resetButtonPlacement() | ||
| 567 | preferences.edit() | 612 | preferences.edit() |
| 568 | .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) | 613 | .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex]) |
| 569 | .apply() | 614 | .apply() |
| 570 | } | 615 | } |
| 571 | 616 | ||
| 572 | fun resetButtonPlacement() { | 617 | private fun resetAllLayouts() { |
| 573 | defaultOverlayByLayout(orientation) | 618 | val editor = preferences.edit() |
| 619 | overlayLayouts.forEachIndexed { i, layout -> | ||
| 620 | defaultOverlayByLayout(layout) | ||
| 621 | editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i]) | ||
| 622 | } | ||
| 623 | editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION) | ||
| 624 | editor.apply() | ||
| 625 | } | ||
| 626 | |||
| 627 | fun resetLayoutVisibilityAndPlacement() { | ||
| 628 | defaultOverlayByLayout(layout) | ||
| 629 | val editor = preferences.edit() | ||
| 630 | Settings.overlayPreferences.forEachIndexed { _, pref -> | ||
| 631 | editor.remove(pref) | ||
| 632 | } | ||
| 633 | editor.apply() | ||
| 574 | refreshControls() | 634 | refreshControls() |
| 575 | } | 635 | } |
| 576 | 636 | ||
| @@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 604 | R.integer.SWITCH_STICK_R_X, | 664 | R.integer.SWITCH_STICK_R_X, |
| 605 | R.integer.SWITCH_STICK_R_Y, | 665 | R.integer.SWITCH_STICK_R_Y, |
| 606 | R.integer.SWITCH_STICK_L_X, | 666 | R.integer.SWITCH_STICK_L_X, |
| 607 | R.integer.SWITCH_STICK_L_Y | 667 | R.integer.SWITCH_STICK_L_Y, |
| 668 | R.integer.SWITCH_BUTTON_STICK_L_X, | ||
| 669 | R.integer.SWITCH_BUTTON_STICK_L_Y, | ||
| 670 | R.integer.SWITCH_BUTTON_STICK_R_X, | ||
| 671 | R.integer.SWITCH_BUTTON_STICK_R_Y | ||
| 608 | ) | 672 | ) |
| 609 | 673 | ||
| 610 | private val portraitResources = arrayOf( | 674 | private val portraitResources = arrayOf( |
| @@ -637,7 +701,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 637 | R.integer.SWITCH_STICK_R_X_PORTRAIT, | 701 | R.integer.SWITCH_STICK_R_X_PORTRAIT, |
| 638 | R.integer.SWITCH_STICK_R_Y_PORTRAIT, | 702 | R.integer.SWITCH_STICK_R_Y_PORTRAIT, |
| 639 | R.integer.SWITCH_STICK_L_X_PORTRAIT, | 703 | R.integer.SWITCH_STICK_L_X_PORTRAIT, |
| 640 | R.integer.SWITCH_STICK_L_Y_PORTRAIT | 704 | R.integer.SWITCH_STICK_L_Y_PORTRAIT, |
| 705 | R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT, | ||
| 706 | R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT, | ||
| 707 | R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT, | ||
| 708 | R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT | ||
| 641 | ) | 709 | ) |
| 642 | 710 | ||
| 643 | private val foldableResources = arrayOf( | 711 | private val foldableResources = arrayOf( |
| @@ -670,139 +738,159 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 670 | R.integer.SWITCH_STICK_R_X_FOLDABLE, | 738 | R.integer.SWITCH_STICK_R_X_FOLDABLE, |
| 671 | R.integer.SWITCH_STICK_R_Y_FOLDABLE, | 739 | R.integer.SWITCH_STICK_R_Y_FOLDABLE, |
| 672 | R.integer.SWITCH_STICK_L_X_FOLDABLE, | 740 | R.integer.SWITCH_STICK_L_X_FOLDABLE, |
| 673 | R.integer.SWITCH_STICK_L_Y_FOLDABLE | 741 | R.integer.SWITCH_STICK_L_Y_FOLDABLE, |
| 742 | R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE, | ||
| 743 | R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE, | ||
| 744 | R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE, | ||
| 745 | R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE | ||
| 674 | ) | 746 | ) |
| 675 | 747 | ||
| 676 | private fun getResourceValue(orientation: String, position: Int): Float { | 748 | private fun getResourceValue(layout: String, position: Int): Float { |
| 677 | return when (orientation) { | 749 | return when (layout) { |
| 678 | PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 | 750 | PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 |
| 679 | FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 | 751 | FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 |
| 680 | else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 | 752 | else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 |
| 681 | } | 753 | } |
| 682 | } | 754 | } |
| 683 | 755 | ||
| 684 | private fun defaultOverlayByLayout(orientation: String) { | 756 | private fun defaultOverlayByLayout(layout: String) { |
| 685 | // Each value represents the position of the button in relation to the screen size without insets. | 757 | // Each value represents the position of the button in relation to the screen size without insets. |
| 686 | preferences.edit() | 758 | preferences.edit() |
| 687 | .putFloat( | 759 | .putFloat( |
| 688 | ButtonType.BUTTON_A.toString() + "-X$orientation", | 760 | "${Settings.PREF_BUTTON_A}-X$layout", |
| 689 | getResourceValue(orientation, 0) | 761 | getResourceValue(layout, 0) |
| 762 | ) | ||
| 763 | .putFloat( | ||
| 764 | "${Settings.PREF_BUTTON_A}-Y$layout", | ||
| 765 | getResourceValue(layout, 1) | ||
| 766 | ) | ||
| 767 | .putFloat( | ||
| 768 | "${Settings.PREF_BUTTON_B}-X$layout", | ||
| 769 | getResourceValue(layout, 2) | ||
| 770 | ) | ||
| 771 | .putFloat( | ||
| 772 | "${Settings.PREF_BUTTON_B}-Y$layout", | ||
| 773 | getResourceValue(layout, 3) | ||
| 774 | ) | ||
| 775 | .putFloat( | ||
| 776 | "${Settings.PREF_BUTTON_X}-X$layout", | ||
| 777 | getResourceValue(layout, 4) | ||
| 690 | ) | 778 | ) |
| 691 | .putFloat( | 779 | .putFloat( |
| 692 | ButtonType.BUTTON_A.toString() + "-Y$orientation", | 780 | "${Settings.PREF_BUTTON_X}-Y$layout", |
| 693 | getResourceValue(orientation, 1) | 781 | getResourceValue(layout, 5) |
| 694 | ) | 782 | ) |
| 695 | .putFloat( | 783 | .putFloat( |
| 696 | ButtonType.BUTTON_B.toString() + "-X$orientation", | 784 | "${Settings.PREF_BUTTON_Y}-X$layout", |
| 697 | getResourceValue(orientation, 2) | 785 | getResourceValue(layout, 6) |
| 698 | ) | 786 | ) |
| 699 | .putFloat( | 787 | .putFloat( |
| 700 | ButtonType.BUTTON_B.toString() + "-Y$orientation", | 788 | "${Settings.PREF_BUTTON_Y}-Y$layout", |
| 701 | getResourceValue(orientation, 3) | 789 | getResourceValue(layout, 7) |
| 702 | ) | 790 | ) |
| 703 | .putFloat( | 791 | .putFloat( |
| 704 | ButtonType.BUTTON_X.toString() + "-X$orientation", | 792 | "${Settings.PREF_BUTTON_ZL}-X$layout", |
| 705 | getResourceValue(orientation, 4) | 793 | getResourceValue(layout, 8) |
| 706 | ) | 794 | ) |
| 707 | .putFloat( | 795 | .putFloat( |
| 708 | ButtonType.BUTTON_X.toString() + "-Y$orientation", | 796 | "${Settings.PREF_BUTTON_ZL}-Y$layout", |
| 709 | getResourceValue(orientation, 5) | 797 | getResourceValue(layout, 9) |
| 710 | ) | 798 | ) |
| 711 | .putFloat( | 799 | .putFloat( |
| 712 | ButtonType.BUTTON_Y.toString() + "-X$orientation", | 800 | "${Settings.PREF_BUTTON_ZR}-X$layout", |
| 713 | getResourceValue(orientation, 6) | 801 | getResourceValue(layout, 10) |
| 714 | ) | 802 | ) |
| 715 | .putFloat( | 803 | .putFloat( |
| 716 | ButtonType.BUTTON_Y.toString() + "-Y$orientation", | 804 | "${Settings.PREF_BUTTON_ZR}-Y$layout", |
| 717 | getResourceValue(orientation, 7) | 805 | getResourceValue(layout, 11) |
| 718 | ) | 806 | ) |
| 719 | .putFloat( | 807 | .putFloat( |
| 720 | ButtonType.TRIGGER_ZL.toString() + "-X$orientation", | 808 | "${Settings.PREF_BUTTON_DPAD}-X$layout", |
| 721 | getResourceValue(orientation, 8) | 809 | getResourceValue(layout, 12) |
| 722 | ) | 810 | ) |
| 723 | .putFloat( | 811 | .putFloat( |
| 724 | ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", | 812 | "${Settings.PREF_BUTTON_DPAD}-Y$layout", |
| 725 | getResourceValue(orientation, 9) | 813 | getResourceValue(layout, 13) |
| 726 | ) | 814 | ) |
| 727 | .putFloat( | 815 | .putFloat( |
| 728 | ButtonType.TRIGGER_ZR.toString() + "-X$orientation", | 816 | "${Settings.PREF_BUTTON_L}-X$layout", |
| 729 | getResourceValue(orientation, 10) | 817 | getResourceValue(layout, 14) |
| 730 | ) | 818 | ) |
| 731 | .putFloat( | 819 | .putFloat( |
| 732 | ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", | 820 | "${Settings.PREF_BUTTON_L}-Y$layout", |
| 733 | getResourceValue(orientation, 11) | 821 | getResourceValue(layout, 15) |
| 734 | ) | 822 | ) |
| 735 | .putFloat( | 823 | .putFloat( |
| 736 | ButtonType.DPAD_UP.toString() + "-X$orientation", | 824 | "${Settings.PREF_BUTTON_R}-X$layout", |
| 737 | getResourceValue(orientation, 12) | 825 | getResourceValue(layout, 16) |
| 738 | ) | 826 | ) |
| 739 | .putFloat( | 827 | .putFloat( |
| 740 | ButtonType.DPAD_UP.toString() + "-Y$orientation", | 828 | "${Settings.PREF_BUTTON_R}-Y$layout", |
| 741 | getResourceValue(orientation, 13) | 829 | getResourceValue(layout, 17) |
| 742 | ) | 830 | ) |
| 743 | .putFloat( | 831 | .putFloat( |
| 744 | ButtonType.TRIGGER_L.toString() + "-X$orientation", | 832 | "${Settings.PREF_BUTTON_PLUS}-X$layout", |
| 745 | getResourceValue(orientation, 14) | 833 | getResourceValue(layout, 18) |
| 746 | ) | 834 | ) |
| 747 | .putFloat( | 835 | .putFloat( |
| 748 | ButtonType.TRIGGER_L.toString() + "-Y$orientation", | 836 | "${Settings.PREF_BUTTON_PLUS}-Y$layout", |
| 749 | getResourceValue(orientation, 15) | 837 | getResourceValue(layout, 19) |
| 750 | ) | 838 | ) |
| 751 | .putFloat( | 839 | .putFloat( |
| 752 | ButtonType.TRIGGER_R.toString() + "-X$orientation", | 840 | "${Settings.PREF_BUTTON_MINUS}-X$layout", |
| 753 | getResourceValue(orientation, 16) | 841 | getResourceValue(layout, 20) |
| 754 | ) | 842 | ) |
| 755 | .putFloat( | 843 | .putFloat( |
| 756 | ButtonType.TRIGGER_R.toString() + "-Y$orientation", | 844 | "${Settings.PREF_BUTTON_MINUS}-Y$layout", |
| 757 | getResourceValue(orientation, 17) | 845 | getResourceValue(layout, 21) |
| 758 | ) | 846 | ) |
| 759 | .putFloat( | 847 | .putFloat( |
| 760 | ButtonType.BUTTON_PLUS.toString() + "-X$orientation", | 848 | "${Settings.PREF_BUTTON_HOME}-X$layout", |
| 761 | getResourceValue(orientation, 18) | 849 | getResourceValue(layout, 22) |
| 762 | ) | 850 | ) |
| 763 | .putFloat( | 851 | .putFloat( |
| 764 | ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", | 852 | "${Settings.PREF_BUTTON_HOME}-Y$layout", |
| 765 | getResourceValue(orientation, 19) | 853 | getResourceValue(layout, 23) |
| 766 | ) | 854 | ) |
| 767 | .putFloat( | 855 | .putFloat( |
| 768 | ButtonType.BUTTON_MINUS.toString() + "-X$orientation", | 856 | "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout", |
| 769 | getResourceValue(orientation, 20) | 857 | getResourceValue(layout, 24) |
| 770 | ) | 858 | ) |
| 771 | .putFloat( | 859 | .putFloat( |
| 772 | ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", | 860 | "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout", |
| 773 | getResourceValue(orientation, 21) | 861 | getResourceValue(layout, 25) |
| 774 | ) | 862 | ) |
| 775 | .putFloat( | 863 | .putFloat( |
| 776 | ButtonType.BUTTON_HOME.toString() + "-X$orientation", | 864 | "${Settings.PREF_STICK_R}-X$layout", |
| 777 | getResourceValue(orientation, 22) | 865 | getResourceValue(layout, 26) |
| 778 | ) | 866 | ) |
| 779 | .putFloat( | 867 | .putFloat( |
| 780 | ButtonType.BUTTON_HOME.toString() + "-Y$orientation", | 868 | "${Settings.PREF_STICK_R}-Y$layout", |
| 781 | getResourceValue(orientation, 23) | 869 | getResourceValue(layout, 27) |
| 782 | ) | 870 | ) |
| 783 | .putFloat( | 871 | .putFloat( |
| 784 | ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", | 872 | "${Settings.PREF_STICK_L}-X$layout", |
| 785 | getResourceValue(orientation, 24) | 873 | getResourceValue(layout, 28) |
| 786 | ) | 874 | ) |
| 787 | .putFloat( | 875 | .putFloat( |
| 788 | ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", | 876 | "${Settings.PREF_STICK_L}-Y$layout", |
| 789 | getResourceValue(orientation, 25) | 877 | getResourceValue(layout, 29) |
| 790 | ) | 878 | ) |
| 791 | .putFloat( | 879 | .putFloat( |
| 792 | ButtonType.STICK_R.toString() + "-X$orientation", | 880 | "${Settings.PREF_BUTTON_STICK_L}-X$layout", |
| 793 | getResourceValue(orientation, 26) | 881 | getResourceValue(layout, 30) |
| 794 | ) | 882 | ) |
| 795 | .putFloat( | 883 | .putFloat( |
| 796 | ButtonType.STICK_R.toString() + "-Y$orientation", | 884 | "${Settings.PREF_BUTTON_STICK_L}-Y$layout", |
| 797 | getResourceValue(orientation, 27) | 885 | getResourceValue(layout, 31) |
| 798 | ) | 886 | ) |
| 799 | .putFloat( | 887 | .putFloat( |
| 800 | ButtonType.STICK_L.toString() + "-X$orientation", | 888 | "${Settings.PREF_BUTTON_STICK_R}-X$layout", |
| 801 | getResourceValue(orientation, 28) | 889 | getResourceValue(layout, 32) |
| 802 | ) | 890 | ) |
| 803 | .putFloat( | 891 | .putFloat( |
| 804 | ButtonType.STICK_L.toString() + "-Y$orientation", | 892 | "${Settings.PREF_BUTTON_STICK_R}-Y$layout", |
| 805 | getResourceValue(orientation, 29) | 893 | getResourceValue(layout, 33) |
| 806 | ) | 894 | ) |
| 807 | .apply() | 895 | .apply() |
| 808 | } | 896 | } |
| @@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 812 | } | 900 | } |
| 813 | 901 | ||
| 814 | companion object { | 902 | companion object { |
| 903 | // Increase this number every time there is a breaking change to every overlay layout | ||
| 904 | const val OVERLAY_VERSION = 1 | ||
| 905 | |||
| 906 | // Increase the corresponding layout version number whenever that layout has a breaking change | ||
| 907 | private const val LANDSCAPE_OVERLAY_VERSION = 1 | ||
| 908 | private const val PORTRAIT_OVERLAY_VERSION = 1 | ||
| 909 | private const val FOLDABLE_OVERLAY_VERSION = 1 | ||
| 910 | val overlayLayoutVersions = listOf( | ||
| 911 | LANDSCAPE_OVERLAY_VERSION, | ||
| 912 | PORTRAIT_OVERLAY_VERSION, | ||
| 913 | FOLDABLE_OVERLAY_VERSION | ||
| 914 | ) | ||
| 915 | |||
| 815 | private val preferences: SharedPreferences = | 916 | private val preferences: SharedPreferences = |
| 816 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 917 | PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 817 | 918 | ||
| 818 | const val LANDSCAPE = "" | 919 | const val LANDSCAPE = "_Landscape" |
| 819 | const val PORTRAIT = "_Portrait" | 920 | const val PORTRAIT = "_Portrait" |
| 820 | const val FOLDABLE = "_Foldable" | 921 | const val FOLDABLE = "_Foldable" |
| 922 | val overlayLayouts = listOf( | ||
| 923 | LANDSCAPE, | ||
| 924 | PORTRAIT, | ||
| 925 | FOLDABLE | ||
| 926 | ) | ||
| 821 | 927 | ||
| 822 | /** | 928 | /** |
| 823 | * Resizes a [Bitmap] by a given scale factor | 929 | * Resizes a [Bitmap] by a given scale factor |
| @@ -948,6 +1054,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 948 | * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). | 1054 | * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). |
| 949 | * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). | 1055 | * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). |
| 950 | * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. | 1056 | * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. |
| 1057 | * @param prefId Identifier for determining where a button appears on screen. | ||
| 1058 | * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. | ||
| 951 | * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. | 1059 | * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. |
| 952 | */ | 1060 | */ |
| 953 | private fun initializeOverlayButton( | 1061 | private fun initializeOverlayButton( |
| @@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 956 | defaultResId: Int, | 1064 | defaultResId: Int, |
| 957 | pressedResId: Int, | 1065 | pressedResId: Int, |
| 958 | buttonId: Int, | 1066 | buttonId: Int, |
| 959 | orientation: String | 1067 | prefId: String, |
| 1068 | layout: String | ||
| 960 | ): InputOverlayDrawableButton { | 1069 | ): InputOverlayDrawableButton { |
| 961 | // Resources handle for fetching the initial Drawable resource. | 1070 | // Resources handle for fetching the initial Drawable resource. |
| 962 | val res = context.resources | 1071 | val res = context.resources |
| @@ -964,17 +1073,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 964 | // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. | 1073 | // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. |
| 965 | val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | 1074 | val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) |
| 966 | 1075 | ||
| 967 | // Decide scale based on button ID and user preference | 1076 | // Decide scale based on button preference ID and user preference |
| 968 | var scale: Float = when (buttonId) { | 1077 | var scale: Float = when (prefId) { |
| 969 | ButtonType.BUTTON_HOME, | 1078 | Settings.PREF_BUTTON_HOME, |
| 970 | ButtonType.BUTTON_CAPTURE, | 1079 | Settings.PREF_BUTTON_SCREENSHOT, |
| 971 | ButtonType.BUTTON_PLUS, | 1080 | Settings.PREF_BUTTON_PLUS, |
| 972 | ButtonType.BUTTON_MINUS -> 0.07f | 1081 | Settings.PREF_BUTTON_MINUS -> 0.07f |
| 973 | 1082 | ||
| 974 | ButtonType.TRIGGER_L, | 1083 | Settings.PREF_BUTTON_L, |
| 975 | ButtonType.TRIGGER_R, | 1084 | Settings.PREF_BUTTON_R, |
| 976 | ButtonType.TRIGGER_ZL, | 1085 | Settings.PREF_BUTTON_ZL, |
| 977 | ButtonType.TRIGGER_ZR -> 0.26f | 1086 | Settings.PREF_BUTTON_ZR -> 0.26f |
| 1087 | |||
| 1088 | Settings.PREF_BUTTON_STICK_L, | ||
| 1089 | Settings.PREF_BUTTON_STICK_R -> 0.155f | ||
| 978 | 1090 | ||
| 979 | else -> 0.11f | 1091 | else -> 0.11f |
| 980 | } | 1092 | } |
| @@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 984 | // Initialize the InputOverlayDrawableButton. | 1096 | // Initialize the InputOverlayDrawableButton. |
| 985 | val defaultStateBitmap = getBitmap(context, defaultResId, scale) | 1097 | val defaultStateBitmap = getBitmap(context, defaultResId, scale) |
| 986 | val pressedStateBitmap = getBitmap(context, pressedResId, scale) | 1098 | val pressedStateBitmap = getBitmap(context, pressedResId, scale) |
| 987 | val overlayDrawable = | 1099 | val overlayDrawable = InputOverlayDrawableButton( |
| 988 | InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) | 1100 | res, |
| 1101 | defaultStateBitmap, | ||
| 1102 | pressedStateBitmap, | ||
| 1103 | buttonId, | ||
| 1104 | prefId | ||
| 1105 | ) | ||
| 989 | 1106 | ||
| 990 | // Get the minimum and maximum coordinates of the screen where the button can be placed. | 1107 | // Get the minimum and maximum coordinates of the screen where the button can be placed. |
| 991 | val min = windowSize.first | 1108 | val min = windowSize.first |
| @@ -993,8 +1110,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 993 | 1110 | ||
| 994 | // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. | 1111 | // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. |
| 995 | // These were set in the input overlay configuration menu. | 1112 | // These were set in the input overlay configuration menu. |
| 996 | val xKey = "$buttonId-X$orientation" | 1113 | val xKey = "$prefId-X$layout" |
| 997 | val yKey = "$buttonId-Y$orientation" | 1114 | val yKey = "$prefId-Y$layout" |
| 998 | val drawableXPercent = sPrefs.getFloat(xKey, 0f) | 1115 | val drawableXPercent = sPrefs.getFloat(xKey, 0f) |
| 999 | val drawableYPercent = sPrefs.getFloat(yKey, 0f) | 1116 | val drawableYPercent = sPrefs.getFloat(yKey, 0f) |
| 1000 | val drawableX = (drawableXPercent * max.x + min.x).toInt() | 1117 | val drawableX = (drawableXPercent * max.x + min.x).toInt() |
| @@ -1029,7 +1146,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1029 | * @param defaultResId The [Bitmap] resource ID of the default state. | 1146 | * @param defaultResId The [Bitmap] resource ID of the default state. |
| 1030 | * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. | 1147 | * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. |
| 1031 | * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. | 1148 | * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. |
| 1032 | * @return the initialized [InputOverlayDrawableDpad] | 1149 | * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. |
| 1150 | * @return The initialized [InputOverlayDrawableDpad] | ||
| 1033 | */ | 1151 | */ |
| 1034 | private fun initializeOverlayDpad( | 1152 | private fun initializeOverlayDpad( |
| 1035 | context: Context, | 1153 | context: Context, |
| @@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1037 | defaultResId: Int, | 1155 | defaultResId: Int, |
| 1038 | pressedOneDirectionResId: Int, | 1156 | pressedOneDirectionResId: Int, |
| 1039 | pressedTwoDirectionsResId: Int, | 1157 | pressedTwoDirectionsResId: Int, |
| 1040 | orientation: String | 1158 | layout: String |
| 1041 | ): InputOverlayDrawableDpad { | 1159 | ): InputOverlayDrawableDpad { |
| 1042 | // Resources handle for fetching the initial Drawable resource. | 1160 | // Resources handle for fetching the initial Drawable resource. |
| 1043 | val res = context.resources | 1161 | val res = context.resources |
| @@ -1074,8 +1192,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1074 | 1192 | ||
| 1075 | // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. | 1193 | // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. |
| 1076 | // These were set in the input overlay configuration menu. | 1194 | // These were set in the input overlay configuration menu. |
| 1077 | val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) | 1195 | val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f) |
| 1078 | val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) | 1196 | val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f) |
| 1079 | val drawableX = (drawableXPercent * max.x + min.x).toInt() | 1197 | val drawableX = (drawableXPercent * max.x + min.x).toInt() |
| 1080 | val drawableY = (drawableYPercent * max.y + min.y).toInt() | 1198 | val drawableY = (drawableYPercent * max.y + min.y).toInt() |
| 1081 | val width = overlayDrawable.width | 1199 | val width = overlayDrawable.width |
| @@ -1107,7 +1225,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1107 | * @param pressedResInner Resource ID for the pressed inner image of the joystick. | 1225 | * @param pressedResInner Resource ID for the pressed inner image of the joystick. |
| 1108 | * @param joystick Identifier for which joystick this is. | 1226 | * @param joystick Identifier for which joystick this is. |
| 1109 | * @param button Identifier for which joystick button this is. | 1227 | * @param button Identifier for which joystick button this is. |
| 1110 | * @return the initialized [InputOverlayDrawableJoystick]. | 1228 | * @param prefId Identifier for determining where a button appears on screen. |
| 1229 | * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. | ||
| 1230 | * @return The initialized [InputOverlayDrawableJoystick]. | ||
| 1111 | */ | 1231 | */ |
| 1112 | private fun initializeOverlayJoystick( | 1232 | private fun initializeOverlayJoystick( |
| 1113 | context: Context, | 1233 | context: Context, |
| @@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1117 | pressedResInner: Int, | 1237 | pressedResInner: Int, |
| 1118 | joystick: Int, | 1238 | joystick: Int, |
| 1119 | button: Int, | 1239 | button: Int, |
| 1120 | orientation: String | 1240 | prefId: String, |
| 1241 | layout: String | ||
| 1121 | ): InputOverlayDrawableJoystick { | 1242 | ): InputOverlayDrawableJoystick { |
| 1122 | // Resources handle for fetching the initial Drawable resource. | 1243 | // Resources handle for fetching the initial Drawable resource. |
| 1123 | val res = context.resources | 1244 | val res = context.resources |
| @@ -1141,8 +1262,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1141 | 1262 | ||
| 1142 | // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. | 1263 | // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. |
| 1143 | // These were set in the input overlay configuration menu. | 1264 | // These were set in the input overlay configuration menu. |
| 1144 | val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) | 1265 | val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f) |
| 1145 | val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) | 1266 | val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f) |
| 1146 | val drawableX = (drawableXPercent * max.x + min.x).toInt() | 1267 | val drawableX = (drawableXPercent * max.x + min.x).toInt() |
| 1147 | val drawableY = (drawableYPercent * max.y + min.y).toInt() | 1268 | val drawableY = (drawableYPercent * max.y + min.y).toInt() |
| 1148 | val outerScale = 1.66f | 1269 | val outerScale = 1.66f |
| @@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : | |||
| 1168 | outerRect, | 1289 | outerRect, |
| 1169 | innerRect, | 1290 | innerRect, |
| 1170 | joystick, | 1291 | joystick, |
| 1171 | button | 1292 | button, |
| 1293 | prefId | ||
| 1172 | ) | 1294 | ) |
| 1173 | 1295 | ||
| 1174 | // Need to set the image's position | 1296 | // Need to set the image's position |
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 4a93e0b14..2c28dda88 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 | |||
| @@ -24,7 +24,8 @@ class InputOverlayDrawableButton( | |||
| 24 | res: Resources, | 24 | res: Resources, |
| 25 | defaultStateBitmap: Bitmap, | 25 | defaultStateBitmap: Bitmap, |
| 26 | pressedStateBitmap: Bitmap, | 26 | pressedStateBitmap: Bitmap, |
| 27 | val buttonId: Int | 27 | val buttonId: Int, |
| 28 | val prefId: String | ||
| 28 | ) { | 29 | ) { |
| 29 | // The ID value what motion event is tracking | 30 | // The ID value what motion event is tracking |
| 30 | var trackId: Int | 31 | 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 fb48f584d..518b1e783 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 | |||
| @@ -37,7 +37,8 @@ class InputOverlayDrawableJoystick( | |||
| 37 | rectOuter: Rect, | 37 | rectOuter: Rect, |
| 38 | rectInner: Rect, | 38 | rectInner: Rect, |
| 39 | val joystickId: Int, | 39 | val joystickId: Int, |
| 40 | val buttonId: Int | 40 | val buttonId: Int, |
| 41 | val prefId: String | ||
| 41 | ) { | 42 | ) { |
| 42 | // The ID value what motion event is tracking | 43 | // The ID value what motion event is tracking |
| 43 | var trackId = -1 | 44 | var trackId = -1 |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index 18e5fa0b0..aa4a5539a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt | |||
| @@ -5,35 +5,101 @@ package org.yuzu.yuzu_emu.utils | |||
| 5 | 5 | ||
| 6 | import android.app.ActivityManager | 6 | import android.app.ActivityManager |
| 7 | import android.content.Context | 7 | import android.content.Context |
| 8 | import android.os.Build | ||
| 8 | import org.yuzu.yuzu_emu.R | 9 | import org.yuzu.yuzu_emu.R |
| 10 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 9 | import java.util.Locale | 11 | import java.util.Locale |
| 12 | import kotlin.math.ceil | ||
| 10 | 13 | ||
| 11 | class MemoryUtil(val context: Context) { | 14 | object MemoryUtil { |
| 15 | private val context get() = YuzuApplication.appContext | ||
| 12 | 16 | ||
| 13 | private val Long.floatForm: String | 17 | private val Float.hundredths: String |
| 14 | get() = String.format(Locale.ROOT, "%.2f", this.toDouble()) | 18 | get() = String.format(Locale.ROOT, "%.2f", this) |
| 15 | 19 | ||
| 16 | private fun bytesToSizeUnit(size: Long): String { | 20 | // Required total system memory |
| 17 | return when { | 21 | const val REQUIRED_MEMORY = 8 |
| 18 | size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" | 22 | |
| 19 | size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}" | 23 | const val Kb: Float = 1024F |
| 20 | size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}" | 24 | const val Mb = Kb * 1024 |
| 21 | size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}" | 25 | const val Gb = Mb * 1024 |
| 22 | size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}" | 26 | const val Tb = Gb * 1024 |
| 23 | size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}" | 27 | const val Pb = Tb * 1024 |
| 24 | else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}" | 28 | const val Eb = Pb * 1024 |
| 29 | |||
| 30 | private fun bytesToSizeUnit(size: Float): String = | ||
| 31 | when { | ||
| 32 | size < Kb -> { | ||
| 33 | context.getString( | ||
| 34 | R.string.memory_formatted, | ||
| 35 | size.hundredths, | ||
| 36 | context.getString(R.string.memory_byte) | ||
| 37 | ) | ||
| 38 | } | ||
| 39 | size < Mb -> { | ||
| 40 | context.getString( | ||
| 41 | R.string.memory_formatted, | ||
| 42 | (size / Kb).hundredths, | ||
| 43 | context.getString(R.string.memory_kilobyte) | ||
| 44 | ) | ||
| 45 | } | ||
| 46 | size < Gb -> { | ||
| 47 | context.getString( | ||
| 48 | R.string.memory_formatted, | ||
| 49 | (size / Mb).hundredths, | ||
| 50 | context.getString(R.string.memory_megabyte) | ||
| 51 | ) | ||
| 52 | } | ||
| 53 | size < Tb -> { | ||
| 54 | context.getString( | ||
| 55 | R.string.memory_formatted, | ||
| 56 | (size / Gb).hundredths, | ||
| 57 | context.getString(R.string.memory_gigabyte) | ||
| 58 | ) | ||
| 59 | } | ||
| 60 | size < Pb -> { | ||
| 61 | context.getString( | ||
| 62 | R.string.memory_formatted, | ||
| 63 | (size / Tb).hundredths, | ||
| 64 | context.getString(R.string.memory_terabyte) | ||
| 65 | ) | ||
| 66 | } | ||
| 67 | size < Eb -> { | ||
| 68 | context.getString( | ||
| 69 | R.string.memory_formatted, | ||
| 70 | (size / Pb).hundredths, | ||
| 71 | context.getString(R.string.memory_petabyte) | ||
| 72 | ) | ||
| 73 | } | ||
| 74 | else -> { | ||
| 75 | context.getString( | ||
| 76 | R.string.memory_formatted, | ||
| 77 | (size / Eb).hundredths, | ||
| 78 | context.getString(R.string.memory_exabyte) | ||
| 79 | ) | ||
| 80 | } | ||
| 25 | } | 81 | } |
| 26 | } | ||
| 27 | 82 | ||
| 28 | private val totalMemory = | 83 | // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for |
| 29 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { | 84 | // the potential error created by memInfo.totalMem |
| 85 | private val totalMemory: Float | ||
| 86 | get() { | ||
| 30 | val memInfo = ActivityManager.MemoryInfo() | 87 | val memInfo = ActivityManager.MemoryInfo() |
| 31 | getMemoryInfo(memInfo) | 88 | with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { |
| 32 | memInfo.totalMem | 89 | getMemoryInfo(memInfo) |
| 90 | } | ||
| 91 | |||
| 92 | return ceil( | ||
| 93 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { | ||
| 94 | memInfo.advertisedMem.toFloat() | ||
| 95 | } else { | ||
| 96 | memInfo.totalMem.toFloat() | ||
| 97 | } | ||
| 98 | ) | ||
| 33 | } | 99 | } |
| 34 | 100 | ||
| 35 | fun isLessThan(minimum: Int, size: Long): Boolean { | 101 | fun isLessThan(minimum: Int, size: Float): Boolean = |
| 36 | return when (size) { | 102 | when (size) { |
| 37 | Kb -> totalMemory < Mb && totalMemory < minimum | 103 | Kb -> totalMemory < Mb && totalMemory < minimum |
| 38 | Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum | 104 | Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum |
| 39 | Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum | 105 | Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum |
| @@ -42,18 +108,6 @@ class MemoryUtil(val context: Context) { | |||
| 42 | Eb -> totalMemory / Eb < minimum | 108 | Eb -> totalMemory / Eb < minimum |
| 43 | else -> totalMemory < Kb && totalMemory < minimum | 109 | else -> totalMemory < Kb && totalMemory < minimum |
| 44 | } | 110 | } |
| 45 | } | 111 | |
| 46 | 112 | fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) | |
| 47 | fun getDeviceRAM(): String { | ||
| 48 | return bytesToSizeUnit(totalMemory) | ||
| 49 | } | ||
| 50 | |||
| 51 | companion object { | ||
| 52 | const val Kb: Long = 1024 | ||
| 53 | const val Mb = Kb * 1024 | ||
| 54 | const val Gb = Mb * 1024 | ||
| 55 | const val Tb = Gb * 1024 | ||
| 56 | const val Pb = Tb * 1024 | ||
| 57 | const val Eb = Pb * 1024 | ||
| 58 | } | ||
| 59 | } | 113 | } |
diff --git a/src/android/app/src/main/res/drawable/button_l3.xml b/src/android/app/src/main/res/drawable/button_l3.xml new file mode 100644 index 000000000..0cb28836e --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_l3.xml | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | xmlns:aapt="http://schemas.android.com/aapt" | ||
| 3 | android:width="34.963dp" | ||
| 4 | android:height="37.265dp" | ||
| 5 | android:viewportWidth="34.963" | ||
| 6 | android:viewportHeight="37.265"> | ||
| 7 | <path | ||
| 8 | android:fillAlpha="0.5" | ||
| 9 | android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" | ||
| 10 | android:strokeAlpha="0.6"> | ||
| 11 | <aapt:attr name="android:fillColor"> | ||
| 12 | <gradient | ||
| 13 | android:endX="21.568" | ||
| 14 | android:endY="33.938" | ||
| 15 | android:startX="21.568" | ||
| 16 | android:startY="16.14" | ||
| 17 | android:type="linear"> | ||
| 18 | <item | ||
| 19 | android:color="#FFC3C4C5" | ||
| 20 | android:offset="0" /> | ||
| 21 | <item | ||
| 22 | android:color="#FFC5C6C6" | ||
| 23 | android:offset="0.03" /> | ||
| 24 | <item | ||
| 25 | android:color="#FFC7C7C7" | ||
| 26 | android:offset="0.19" /> | ||
| 27 | <item | ||
| 28 | android:color="#DBB5B5B5" | ||
| 29 | android:offset="0.44" /> | ||
| 30 | <item | ||
| 31 | android:color="#7F878787" | ||
| 32 | android:offset="1" /> | ||
| 33 | </gradient> | ||
| 34 | </aapt:attr> | ||
| 35 | </path> | ||
| 36 | <path | ||
| 37 | android:fillAlpha="0.5" | ||
| 38 | android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" | ||
| 39 | android:strokeAlpha="0.6"> | ||
| 40 | <aapt:attr name="android:fillColor"> | ||
| 41 | <gradient | ||
| 42 | android:endX="17.395" | ||
| 43 | android:endY="18.74" | ||
| 44 | android:startX="17.395" | ||
| 45 | android:startY="-1.296" | ||
| 46 | android:type="linear"> | ||
| 47 | <item | ||
| 48 | android:color="#FFC3C4C5" | ||
| 49 | android:offset="0" /> | ||
| 50 | <item | ||
| 51 | android:color="#FFC5C6C6" | ||
| 52 | android:offset="0.03" /> | ||
| 53 | <item | ||
| 54 | android:color="#FFC7C7C7" | ||
| 55 | android:offset="0.19" /> | ||
| 56 | <item | ||
| 57 | android:color="#DBB5B5B5" | ||
| 58 | android:offset="0.44" /> | ||
| 59 | <item | ||
| 60 | android:color="#7F878787" | ||
| 61 | android:offset="1" /> | ||
| 62 | </gradient> | ||
| 63 | </aapt:attr> | ||
| 64 | </path> | ||
| 65 | <path | ||
| 66 | android:fillAlpha="0.5" | ||
| 67 | android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" | ||
| 68 | android:strokeAlpha="0.6"> | ||
| 69 | <aapt:attr name="android:fillColor"> | ||
| 70 | <gradient | ||
| 71 | android:centerX="17.477" | ||
| 72 | android:centerY="19.92" | ||
| 73 | android:gradientRadius="17.201" | ||
| 74 | android:type="radial"> | ||
| 75 | <item | ||
| 76 | android:color="#FFC3C4C5" | ||
| 77 | android:offset="0.58" /> | ||
| 78 | <item | ||
| 79 | android:color="#FFC6C6C6" | ||
| 80 | android:offset="0.84" /> | ||
| 81 | <item | ||
| 82 | android:color="#FFC7C7C7" | ||
| 83 | android:offset="0.88" /> | ||
| 84 | <item | ||
| 85 | android:color="#FFC2C2C2" | ||
| 86 | android:offset="0.91" /> | ||
| 87 | <item | ||
| 88 | android:color="#FFB5B5B5" | ||
| 89 | android:offset="0.94" /> | ||
| 90 | <item | ||
| 91 | android:color="#FF9E9E9E" | ||
| 92 | android:offset="0.98" /> | ||
| 93 | <item | ||
| 94 | android:color="#FF8F8F8F" | ||
| 95 | android:offset="1" /> | ||
| 96 | </gradient> | ||
| 97 | </aapt:attr> | ||
| 98 | </path> | ||
| 99 | <path | ||
| 100 | android:fillAlpha="0.5" | ||
| 101 | android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z" | ||
| 102 | android:strokeAlpha="0.6"> | ||
| 103 | <aapt:attr name="android:fillColor"> | ||
| 104 | <gradient | ||
| 105 | android:endX="16.829" | ||
| 106 | android:endY="46.882" | ||
| 107 | android:startX="16.829" | ||
| 108 | android:startY="20.479" | ||
| 109 | android:type="linear"> | ||
| 110 | <item | ||
| 111 | android:color="#FFC3C4C5" | ||
| 112 | android:offset="0" /> | ||
| 113 | <item | ||
| 114 | android:color="#FFC5C6C6" | ||
| 115 | android:offset="0.03" /> | ||
| 116 | <item | ||
| 117 | android:color="#FFC7C7C7" | ||
| 118 | android:offset="0.19" /> | ||
| 119 | <item | ||
| 120 | android:color="#DBB5B5B5" | ||
| 121 | android:offset="0.44" /> | ||
| 122 | <item | ||
| 123 | android:color="#7F878787" | ||
| 124 | android:offset="1" /> | ||
| 125 | </gradient> | ||
| 126 | </aapt:attr> | ||
| 127 | </path> | ||
| 128 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/button_l3_depressed.xml b/src/android/app/src/main/res/drawable/button_l3_depressed.xml new file mode 100644 index 000000000..b078dedc9 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_l3_depressed.xml | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | xmlns:aapt="http://schemas.android.com/aapt" | ||
| 3 | android:width="34.963dp" | ||
| 4 | android:height="37.265dp" | ||
| 5 | android:viewportWidth="34.963" | ||
| 6 | android:viewportHeight="37.265"> | ||
| 7 | <path | ||
| 8 | android:fillAlpha="0.3" | ||
| 9 | android:fillColor="#151515" | ||
| 10 | android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" | ||
| 11 | android:strokeAlpha="0.3" /> | ||
| 12 | <path | ||
| 13 | android:fillAlpha="0.6" | ||
| 14 | android:fillColor="#151515" | ||
| 15 | android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" | ||
| 16 | android:strokeAlpha="0.6" /> | ||
| 17 | <path | ||
| 18 | android:fillAlpha="0.6" | ||
| 19 | android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" | ||
| 20 | android:strokeAlpha="0.6"> | ||
| 21 | <aapt:attr name="android:fillColor"> | ||
| 22 | <gradient | ||
| 23 | android:endX="21.568" | ||
| 24 | android:endY="33.938" | ||
| 25 | android:startX="21.568" | ||
| 26 | android:startY="16.14" | ||
| 27 | android:type="linear"> | ||
| 28 | <item | ||
| 29 | android:color="#FFC3C4C5" | ||
| 30 | android:offset="0" /> | ||
| 31 | <item | ||
| 32 | android:color="#FFC5C6C6" | ||
| 33 | android:offset="0.03" /> | ||
| 34 | <item | ||
| 35 | android:color="#FFC7C7C7" | ||
| 36 | android:offset="0.19" /> | ||
| 37 | <item | ||
| 38 | android:color="#DBB5B5B5" | ||
| 39 | android:offset="0.44" /> | ||
| 40 | <item | ||
| 41 | android:color="#7F878787" | ||
| 42 | android:offset="1" /> | ||
| 43 | </gradient> | ||
| 44 | </aapt:attr> | ||
| 45 | </path> | ||
| 46 | <path | ||
| 47 | android:fillAlpha="0.6" | ||
| 48 | android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z" | ||
| 49 | android:strokeAlpha="0.6"> | ||
| 50 | <aapt:attr name="android:fillColor"> | ||
| 51 | <gradient | ||
| 52 | android:endX="16.829" | ||
| 53 | android:endY="46.882" | ||
| 54 | android:startX="16.829" | ||
| 55 | android:startY="20.479" | ||
| 56 | android:type="linear"> | ||
| 57 | <item | ||
| 58 | android:color="#FFC3C4C5" | ||
| 59 | android:offset="0" /> | ||
| 60 | <item | ||
| 61 | android:color="#FFC5C6C6" | ||
| 62 | android:offset="0.03" /> | ||
| 63 | <item | ||
| 64 | android:color="#FFC7C7C7" | ||
| 65 | android:offset="0.19" /> | ||
| 66 | <item | ||
| 67 | android:color="#DBB5B5B5" | ||
| 68 | android:offset="0.44" /> | ||
| 69 | <item | ||
| 70 | android:color="#7F878787" | ||
| 71 | android:offset="1" /> | ||
| 72 | </gradient> | ||
| 73 | </aapt:attr> | ||
| 74 | </path> | ||
| 75 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/button_r3.xml b/src/android/app/src/main/res/drawable/button_r3.xml new file mode 100644 index 000000000..5c6864e26 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_r3.xml | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | xmlns:aapt="http://schemas.android.com/aapt" | ||
| 3 | android:width="34.963dp" | ||
| 4 | android:height="37.265dp" | ||
| 5 | android:viewportWidth="34.963" | ||
| 6 | android:viewportHeight="37.265"> | ||
| 7 | <path | ||
| 8 | android:fillAlpha="0.5" | ||
| 9 | android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z" | ||
| 10 | android:strokeAlpha="0.6"> | ||
| 11 | <aapt:attr name="android:fillColor"> | ||
| 12 | <gradient | ||
| 13 | android:endX="15.506" | ||
| 14 | android:endY="48.977" | ||
| 15 | android:startX="15.506" | ||
| 16 | android:startY="19.659" | ||
| 17 | android:type="linear"> | ||
| 18 | <item | ||
| 19 | android:color="#FFC3C4C5" | ||
| 20 | android:offset="0" /> | ||
| 21 | <item | ||
| 22 | android:color="#FFC5C6C6" | ||
| 23 | android:offset="0.03" /> | ||
| 24 | <item | ||
| 25 | android:color="#FFC7C7C7" | ||
| 26 | android:offset="0.19" /> | ||
| 27 | <item | ||
| 28 | android:color="#DBB5B5B5" | ||
| 29 | android:offset="0.44" /> | ||
| 30 | <item | ||
| 31 | android:color="#7F878787" | ||
| 32 | android:offset="1" /> | ||
| 33 | </gradient> | ||
| 34 | </aapt:attr> | ||
| 35 | </path> | ||
| 36 | <path | ||
| 37 | android:fillAlpha="0.5" | ||
| 38 | android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" | ||
| 39 | android:strokeAlpha="0.6"> | ||
| 40 | <aapt:attr name="android:fillColor"> | ||
| 41 | <gradient | ||
| 42 | android:endX="17.395" | ||
| 43 | android:endY="18.74" | ||
| 44 | android:startX="17.395" | ||
| 45 | android:startY="-1.296" | ||
| 46 | android:type="linear"> | ||
| 47 | <item | ||
| 48 | android:color="#FFC3C4C5" | ||
| 49 | android:offset="0" /> | ||
| 50 | <item | ||
| 51 | android:color="#FFC5C6C6" | ||
| 52 | android:offset="0.03" /> | ||
| 53 | <item | ||
| 54 | android:color="#FFC7C7C7" | ||
| 55 | android:offset="0.19" /> | ||
| 56 | <item | ||
| 57 | android:color="#DBB5B5B5" | ||
| 58 | android:offset="0.44" /> | ||
| 59 | <item | ||
| 60 | android:color="#7F878787" | ||
| 61 | android:offset="1" /> | ||
| 62 | </gradient> | ||
| 63 | </aapt:attr> | ||
| 64 | </path> | ||
| 65 | <path | ||
| 66 | android:fillAlpha="0.5" | ||
| 67 | android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" | ||
| 68 | android:strokeAlpha="0.6"> | ||
| 69 | <aapt:attr name="android:fillColor"> | ||
| 70 | <gradient | ||
| 71 | android:centerX="17.477" | ||
| 72 | android:centerY="19.92" | ||
| 73 | android:gradientRadius="17.201" | ||
| 74 | android:type="radial"> | ||
| 75 | <item | ||
| 76 | android:color="#FFC3C4C5" | ||
| 77 | android:offset="0.58" /> | ||
| 78 | <item | ||
| 79 | android:color="#FFC6C6C6" | ||
| 80 | android:offset="0.84" /> | ||
| 81 | <item | ||
| 82 | android:color="#FFC7C7C7" | ||
| 83 | android:offset="0.88" /> | ||
| 84 | <item | ||
| 85 | android:color="#FFC2C2C2" | ||
| 86 | android:offset="0.91" /> | ||
| 87 | <item | ||
| 88 | android:color="#FFB5B5B5" | ||
| 89 | android:offset="0.94" /> | ||
| 90 | <item | ||
| 91 | android:color="#FF9E9E9E" | ||
| 92 | android:offset="0.98" /> | ||
| 93 | <item | ||
| 94 | android:color="#FF8F8F8F" | ||
| 95 | android:offset="1" /> | ||
| 96 | </gradient> | ||
| 97 | </aapt:attr> | ||
| 98 | </path> | ||
| 99 | <path | ||
| 100 | android:fillAlpha="0.5" | ||
| 101 | android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" | ||
| 102 | android:strokeAlpha="0.6"> | ||
| 103 | <aapt:attr name="android:fillColor"> | ||
| 104 | <gradient | ||
| 105 | android:endX="23.949" | ||
| 106 | android:endY="33.938" | ||
| 107 | android:startX="23.949" | ||
| 108 | android:startY="16.14" | ||
| 109 | android:type="linear"> | ||
| 110 | <item | ||
| 111 | android:color="#FFC3C4C5" | ||
| 112 | android:offset="0" /> | ||
| 113 | <item | ||
| 114 | android:color="#FFC5C6C6" | ||
| 115 | android:offset="0.03" /> | ||
| 116 | <item | ||
| 117 | android:color="#FFC7C7C7" | ||
| 118 | android:offset="0.19" /> | ||
| 119 | <item | ||
| 120 | android:color="#DBB5B5B5" | ||
| 121 | android:offset="0.44" /> | ||
| 122 | <item | ||
| 123 | android:color="#7F878787" | ||
| 124 | android:offset="1" /> | ||
| 125 | </gradient> | ||
| 126 | </aapt:attr> | ||
| 127 | </path> | ||
| 128 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/button_r3_depressed.xml b/src/android/app/src/main/res/drawable/button_r3_depressed.xml new file mode 100644 index 000000000..20f480179 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_r3_depressed.xml | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | xmlns:aapt="http://schemas.android.com/aapt" | ||
| 3 | android:width="34.963dp" | ||
| 4 | android:height="37.265dp" | ||
| 5 | android:viewportWidth="34.963" | ||
| 6 | android:viewportHeight="37.265"> | ||
| 7 | <path | ||
| 8 | android:fillAlpha="0.3" | ||
| 9 | android:fillColor="#151515" | ||
| 10 | android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" | ||
| 11 | android:strokeAlpha="0.3" /> | ||
| 12 | <path | ||
| 13 | android:fillAlpha="0.6" | ||
| 14 | android:fillColor="#151515" | ||
| 15 | android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" | ||
| 16 | android:strokeAlpha="0.6" /> | ||
| 17 | <path | ||
| 18 | android:fillAlpha="0.6" | ||
| 19 | android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z" | ||
| 20 | android:strokeAlpha="0.6"> | ||
| 21 | <aapt:attr name="android:fillColor"> | ||
| 22 | <gradient | ||
| 23 | android:endX="15.506" | ||
| 24 | android:endY="48.977" | ||
| 25 | android:startX="15.506" | ||
| 26 | android:startY="19.659" | ||
| 27 | android:type="linear"> | ||
| 28 | <item | ||
| 29 | android:color="#FFC3C4C5" | ||
| 30 | android:offset="0" /> | ||
| 31 | <item | ||
| 32 | android:color="#FFC5C6C6" | ||
| 33 | android:offset="0.03" /> | ||
| 34 | <item | ||
| 35 | android:color="#FFC7C7C7" | ||
| 36 | android:offset="0.19" /> | ||
| 37 | <item | ||
| 38 | android:color="#DBB5B5B5" | ||
| 39 | android:offset="0.44" /> | ||
| 40 | <item | ||
| 41 | android:color="#7F878787" | ||
| 42 | android:offset="1" /> | ||
| 43 | </gradient> | ||
| 44 | </aapt:attr> | ||
| 45 | </path> | ||
| 46 | <path | ||
| 47 | android:fillAlpha="0.6" | ||
| 48 | android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" | ||
| 49 | android:strokeAlpha="0.6"> | ||
| 50 | <aapt:attr name="android:fillColor"> | ||
| 51 | <gradient | ||
| 52 | android:endX="23.949" | ||
| 53 | android:endY="33.938" | ||
| 54 | android:startX="23.949" | ||
| 55 | android:startY="16.14" | ||
| 56 | android:type="linear"> | ||
| 57 | <item | ||
| 58 | android:color="#FFC3C4C5" | ||
| 59 | android:offset="0" /> | ||
| 60 | <item | ||
| 61 | android:color="#FFC5C6C6" | ||
| 62 | android:offset="0.03" /> | ||
| 63 | <item | ||
| 64 | android:color="#FFC7C7C7" | ||
| 65 | android:offset="0.19" /> | ||
| 66 | <item | ||
| 67 | android:color="#DBB5B5B5" | ||
| 68 | android:offset="0.44" /> | ||
| 69 | <item | ||
| 70 | android:color="#7F878787" | ||
| 71 | android:offset="1" /> | ||
| 72 | </gradient> | ||
| 73 | </aapt:attr> | ||
| 74 | </path> | ||
| 75 | </vector> | ||
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 6d092f7a9..200b99185 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -205,6 +205,8 @@ | |||
| 205 | <item>@string/gamepad_d_pad</item> | 205 | <item>@string/gamepad_d_pad</item> |
| 206 | <item>@string/gamepad_left_stick</item> | 206 | <item>@string/gamepad_left_stick</item> |
| 207 | <item>@string/gamepad_right_stick</item> | 207 | <item>@string/gamepad_right_stick</item> |
| 208 | <item>L3</item> | ||
| 209 | <item>R3</item> | ||
| 208 | <item>@string/gamepad_home</item> | 210 | <item>@string/gamepad_home</item> |
| 209 | <item>@string/gamepad_screenshot</item> | 211 | <item>@string/gamepad_screenshot</item> |
| 210 | </string-array> | 212 | </string-array> |
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml index 2e93b408c..5e39bc7d9 100644 --- a/src/android/app/src/main/res/values/integers.xml +++ b/src/android/app/src/main/res/values/integers.xml | |||
| @@ -33,6 +33,10 @@ | |||
| 33 | <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> | 33 | <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> |
| 34 | <integer name="SWITCH_BUTTON_DPAD_X">260</integer> | 34 | <integer name="SWITCH_BUTTON_DPAD_X">260</integer> |
| 35 | <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> | 35 | <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> |
| 36 | <integer name="SWITCH_BUTTON_STICK_L_X">870</integer> | ||
| 37 | <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer> | ||
| 38 | <integer name="SWITCH_BUTTON_STICK_R_X">960</integer> | ||
| 39 | <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer> | ||
| 36 | 40 | ||
| 37 | <!-- Default SWITCH portrait layout --> | 41 | <!-- Default SWITCH portrait layout --> |
| 38 | <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> | 42 | <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> |
| @@ -65,6 +69,10 @@ | |||
| 65 | <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> | 69 | <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> |
| 66 | <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> | 70 | <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> |
| 67 | <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> | 71 | <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> |
| 72 | <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer> | ||
| 73 | <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer> | ||
| 74 | <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer> | ||
| 75 | <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer> | ||
| 68 | 76 | ||
| 69 | <!-- Default SWITCH foldable layout --> | 77 | <!-- Default SWITCH foldable layout --> |
| 70 | <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> | 78 | <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> |
| @@ -97,5 +105,9 @@ | |||
| 97 | <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> | 105 | <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> |
| 98 | <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> | 106 | <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> |
| 99 | <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> | 107 | <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> |
| 108 | <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer> | ||
| 109 | <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer> | ||
| 110 | <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer> | ||
| 111 | <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer> | ||
| 100 | 112 | ||
| 101 | </resources> | 113 | </resources> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index af7450619..b3c737979 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -273,6 +273,7 @@ | |||
| 273 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> | 273 | <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> |
| 274 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> | 274 | <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> |
| 275 | <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> | 275 | <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> |
| 276 | <string name="memory_formatted">%1$s %2$s</string> | ||
| 276 | 277 | ||
| 277 | <!-- Region Names --> | 278 | <!-- Region Names --> |
| 278 | <string name="region_japan">Japan</string> | 279 | <string name="region_japan">Japan</string> |
diff --git a/src/core/core.cpp b/src/core/core.cpp index b74fd0a58..9e3eb3795 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "core/file_sys/savedata_factory.h" | 27 | #include "core/file_sys/savedata_factory.h" |
| 28 | #include "core/file_sys/vfs_concat.h" | 28 | #include "core/file_sys/vfs_concat.h" |
| 29 | #include "core/file_sys/vfs_real.h" | 29 | #include "core/file_sys/vfs_real.h" |
| 30 | #include "core/gpu_dirty_memory_manager.h" | ||
| 30 | #include "core/hid/hid_core.h" | 31 | #include "core/hid/hid_core.h" |
| 31 | #include "core/hle/kernel/k_memory_manager.h" | 32 | #include "core/hle/kernel/k_memory_manager.h" |
| 32 | #include "core/hle/kernel/k_process.h" | 33 | #include "core/hle/kernel/k_process.h" |
| @@ -130,7 +131,10 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 130 | struct System::Impl { | 131 | struct System::Impl { |
| 131 | explicit Impl(System& system) | 132 | explicit Impl(System& system) |
| 132 | : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, | 133 | : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, |
| 133 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} | 134 | cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system}, |
| 135 | gpu_dirty_memory_write_manager{} { | ||
| 136 | memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager); | ||
| 137 | } | ||
| 134 | 138 | ||
| 135 | void Initialize(System& system) { | 139 | void Initialize(System& system) { |
| 136 | device_memory = std::make_unique<Core::DeviceMemory>(); | 140 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| @@ -234,6 +238,8 @@ struct System::Impl { | |||
| 234 | // Setting changes may require a full system reinitialization (e.g., disabling multicore). | 238 | // Setting changes may require a full system reinitialization (e.g., disabling multicore). |
| 235 | ReinitializeIfNecessary(system); | 239 | ReinitializeIfNecessary(system); |
| 236 | 240 | ||
| 241 | memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager); | ||
| 242 | |||
| 237 | kernel.Initialize(); | 243 | kernel.Initialize(); |
| 238 | cpu_manager.Initialize(); | 244 | cpu_manager.Initialize(); |
| 239 | 245 | ||
| @@ -540,6 +546,9 @@ struct System::Impl { | |||
| 540 | 546 | ||
| 541 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; | 547 | std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; |
| 542 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; | 548 | std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; |
| 549 | |||
| 550 | std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> | ||
| 551 | gpu_dirty_memory_write_manager{}; | ||
| 543 | }; | 552 | }; |
| 544 | 553 | ||
| 545 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 554 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| @@ -629,10 +638,31 @@ void System::PrepareReschedule(const u32 core_index) { | |||
| 629 | impl->kernel.PrepareReschedule(core_index); | 638 | impl->kernel.PrepareReschedule(core_index); |
| 630 | } | 639 | } |
| 631 | 640 | ||
| 641 | Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() { | ||
| 642 | const std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 643 | return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES | ||
| 644 | ? core | ||
| 645 | : Core::Hardware::NUM_CPU_CORES - 1]; | ||
| 646 | } | ||
| 647 | |||
| 648 | /// Provides a constant reference to the current gou dirty memory manager. | ||
| 649 | const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const { | ||
| 650 | const std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 651 | return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES | ||
| 652 | ? core | ||
| 653 | : Core::Hardware::NUM_CPU_CORES - 1]; | ||
| 654 | } | ||
| 655 | |||
| 632 | size_t System::GetCurrentHostThreadID() const { | 656 | size_t System::GetCurrentHostThreadID() const { |
| 633 | return impl->kernel.GetCurrentHostThreadID(); | 657 | return impl->kernel.GetCurrentHostThreadID(); |
| 634 | } | 658 | } |
| 635 | 659 | ||
| 660 | void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { | ||
| 661 | for (auto& manager : impl->gpu_dirty_memory_write_manager) { | ||
| 662 | manager.Gather(callback); | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 636 | PerfStatsResults System::GetAndResetPerfStats() { | 666 | PerfStatsResults System::GetAndResetPerfStats() { |
| 637 | return impl->GetAndResetPerfStats(); | 667 | return impl->GetAndResetPerfStats(); |
| 638 | } | 668 | } |
diff --git a/src/core/core.h b/src/core/core.h index 93afc9303..14b2f7785 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -108,9 +108,10 @@ class CpuManager; | |||
| 108 | class Debugger; | 108 | class Debugger; |
| 109 | class DeviceMemory; | 109 | class DeviceMemory; |
| 110 | class ExclusiveMonitor; | 110 | class ExclusiveMonitor; |
| 111 | class SpeedLimiter; | 111 | class GPUDirtyMemoryManager; |
| 112 | class PerfStats; | 112 | class PerfStats; |
| 113 | class Reporter; | 113 | class Reporter; |
| 114 | class SpeedLimiter; | ||
| 114 | class TelemetrySession; | 115 | class TelemetrySession; |
| 115 | 116 | ||
| 116 | struct PerfStatsResults; | 117 | struct PerfStatsResults; |
| @@ -225,6 +226,14 @@ public: | |||
| 225 | /// Prepare the core emulation for a reschedule | 226 | /// Prepare the core emulation for a reschedule |
| 226 | void PrepareReschedule(u32 core_index); | 227 | void PrepareReschedule(u32 core_index); |
| 227 | 228 | ||
| 229 | /// Provides a reference to the gou dirty memory manager. | ||
| 230 | [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager(); | ||
| 231 | |||
| 232 | /// Provides a constant reference to the current gou dirty memory manager. | ||
| 233 | [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const; | ||
| 234 | |||
| 235 | void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); | ||
| 236 | |||
| 228 | [[nodiscard]] size_t GetCurrentHostThreadID() const; | 237 | [[nodiscard]] size_t GetCurrentHostThreadID() const; |
| 229 | 238 | ||
| 230 | /// Gets and resets core performance statistics | 239 | /// Gets and resets core performance statistics |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 4f0a3f8ea..e6112a3c9 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -253,9 +253,6 @@ void CoreTiming::ThreadLoop() { | |||
| 253 | auto wait_time = *next_time - GetGlobalTimeNs().count(); | 253 | auto wait_time = *next_time - GetGlobalTimeNs().count(); |
| 254 | if (wait_time > 0) { | 254 | if (wait_time > 0) { |
| 255 | #ifdef _WIN32 | 255 | #ifdef _WIN32 |
| 256 | const auto timer_resolution_ns = | ||
| 257 | Common::Windows::GetCurrentTimerResolution().count(); | ||
| 258 | |||
| 259 | while (!paused && !event.IsSet() && wait_time > 0) { | 256 | while (!paused && !event.IsSet() && wait_time > 0) { |
| 260 | wait_time = *next_time - GetGlobalTimeNs().count(); | 257 | wait_time = *next_time - GetGlobalTimeNs().count(); |
| 261 | 258 | ||
| @@ -316,4 +313,10 @@ std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | |||
| 316 | return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; | 313 | return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)}; |
| 317 | } | 314 | } |
| 318 | 315 | ||
| 316 | #ifdef _WIN32 | ||
| 317 | void CoreTiming::SetTimerResolutionNs(std::chrono::nanoseconds ns) { | ||
| 318 | timer_resolution_ns = ns.count(); | ||
| 319 | } | ||
| 320 | #endif | ||
| 321 | |||
| 319 | } // namespace Core::Timing | 322 | } // namespace Core::Timing |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 10db1de55..5bca1c78d 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -131,6 +131,10 @@ public: | |||
| 131 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. | 131 | /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. |
| 132 | std::optional<s64> Advance(); | 132 | std::optional<s64> Advance(); |
| 133 | 133 | ||
| 134 | #ifdef _WIN32 | ||
| 135 | void SetTimerResolutionNs(std::chrono::nanoseconds ns); | ||
| 136 | #endif | ||
| 137 | |||
| 134 | private: | 138 | private: |
| 135 | struct Event; | 139 | struct Event; |
| 136 | 140 | ||
| @@ -143,6 +147,10 @@ private: | |||
| 143 | 147 | ||
| 144 | s64 global_timer = 0; | 148 | s64 global_timer = 0; |
| 145 | 149 | ||
| 150 | #ifdef _WIN32 | ||
| 151 | s64 timer_resolution_ns; | ||
| 152 | #endif | ||
| 153 | |||
| 146 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 154 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 147 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 155 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| 148 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't | 156 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't |
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 1ff83c08c..e39c7b62b 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp | |||
| @@ -105,19 +105,11 @@ static u64 romfs_get_hash_table_count(u64 num_entries) { | |||
| 105 | return count; | 105 | return count; |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir, | 108 | void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir, |
| 109 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { | 109 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { |
| 110 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; | 110 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; |
| 111 | 111 | ||
| 112 | VirtualDir dir; | 112 | const auto entries = romfs_dir->GetEntries(); |
| 113 | |||
| 114 | if (parent->path_len == 0) { | ||
| 115 | dir = root_romfs; | ||
| 116 | } else { | ||
| 117 | dir = root_romfs->GetDirectoryRelative(parent->path); | ||
| 118 | } | ||
| 119 | |||
| 120 | const auto entries = dir->GetEntries(); | ||
| 121 | 113 | ||
| 122 | for (const auto& kv : entries) { | 114 | for (const auto& kv : entries) { |
| 123 | if (kv.second == VfsEntryType::Directory) { | 115 | if (kv.second == VfsEntryType::Directory) { |
| @@ -127,7 +119,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 127 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 119 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 128 | child->path = parent->path + "/" + kv.first; | 120 | child->path = parent->path + "/" + kv.first; |
| 129 | 121 | ||
| 130 | if (ext_dir != nullptr && ext_dir->GetFileRelative(child->path + ".stub") != nullptr) { | 122 | if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { |
| 131 | continue; | 123 | continue; |
| 132 | } | 124 | } |
| 133 | 125 | ||
| @@ -144,17 +136,17 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 144 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | 136 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); |
| 145 | child->path = parent->path + "/" + kv.first; | 137 | child->path = parent->path + "/" + kv.first; |
| 146 | 138 | ||
| 147 | if (ext_dir != nullptr && ext_dir->GetFileRelative(child->path + ".stub") != nullptr) { | 139 | if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { |
| 148 | continue; | 140 | continue; |
| 149 | } | 141 | } |
| 150 | 142 | ||
| 151 | // Sanity check on path_len | 143 | // Sanity check on path_len |
| 152 | ASSERT(child->path_len < FS_MAX_PATH); | 144 | ASSERT(child->path_len < FS_MAX_PATH); |
| 153 | 145 | ||
| 154 | child->source = root_romfs->GetFileRelative(child->path); | 146 | child->source = romfs_dir->GetFile(kv.first); |
| 155 | 147 | ||
| 156 | if (ext_dir != nullptr) { | 148 | if (ext_dir != nullptr) { |
| 157 | if (const auto ips = ext_dir->GetFileRelative(child->path + ".ips")) { | 149 | if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) { |
| 158 | if (auto patched = PatchIPS(child->source, ips)) { | 150 | if (auto patched = PatchIPS(child->source, ips)) { |
| 159 | child->source = std::move(patched); | 151 | child->source = std::move(patched); |
| 160 | } | 152 | } |
| @@ -168,23 +160,27 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext_dir | |||
| 168 | } | 160 | } |
| 169 | 161 | ||
| 170 | for (auto& child : child_dirs) { | 162 | for (auto& child : child_dirs) { |
| 171 | this->VisitDirectory(root_romfs, ext_dir, child); | 163 | auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs); |
| 164 | auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name); | ||
| 165 | auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr; | ||
| 166 | this->VisitDirectory(child_romfs_dir, child_ext_dir, child); | ||
| 172 | } | 167 | } |
| 173 | } | 168 | } |
| 174 | 169 | ||
| 175 | bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | 170 | bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, |
| 176 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { | 171 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { |
| 177 | // Check whether it's already in the known directories. | 172 | // Check whether it's already in the known directories. |
| 178 | const auto existing = directories.find(dir_ctx->path); | 173 | const auto [it, is_new] = directories.emplace(dir_ctx->path, nullptr); |
| 179 | if (existing != directories.end()) | 174 | if (!is_new) { |
| 180 | return false; | 175 | return false; |
| 176 | } | ||
| 181 | 177 | ||
| 182 | // Add a new directory. | 178 | // Add a new directory. |
| 183 | num_dirs++; | 179 | num_dirs++; |
| 184 | dir_table_size += | 180 | dir_table_size += |
| 185 | sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); | 181 | sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); |
| 186 | dir_ctx->parent = parent_dir_ctx; | 182 | dir_ctx->parent = parent_dir_ctx; |
| 187 | directories.emplace(dir_ctx->path, dir_ctx); | 183 | it->second = dir_ctx; |
| 188 | 184 | ||
| 189 | return true; | 185 | return true; |
| 190 | } | 186 | } |
| @@ -192,8 +188,8 @@ bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> | |||
| 192 | bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | 188 | bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, |
| 193 | std::shared_ptr<RomFSBuildFileContext> file_ctx) { | 189 | std::shared_ptr<RomFSBuildFileContext> file_ctx) { |
| 194 | // Check whether it's already in the known files. | 190 | // Check whether it's already in the known files. |
| 195 | const auto existing = files.find(file_ctx->path); | 191 | const auto [it, is_new] = files.emplace(file_ctx->path, nullptr); |
| 196 | if (existing != files.end()) { | 192 | if (!is_new) { |
| 197 | return false; | 193 | return false; |
| 198 | } | 194 | } |
| 199 | 195 | ||
| @@ -202,7 +198,7 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare | |||
| 202 | file_table_size += | 198 | file_table_size += |
| 203 | sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); | 199 | sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); |
| 204 | file_ctx->parent = parent_dir_ctx; | 200 | file_ctx->parent = parent_dir_ctx; |
| 205 | files.emplace(file_ctx->path, file_ctx); | 201 | it->second = file_ctx; |
| 206 | 202 | ||
| 207 | return true; | 203 | return true; |
| 208 | } | 204 | } |
diff --git a/src/core/gpu_dirty_memory_manager.h b/src/core/gpu_dirty_memory_manager.h new file mode 100644 index 000000000..9687531e8 --- /dev/null +++ b/src/core/gpu_dirty_memory_manager.h | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <atomic> | ||
| 7 | #include <bit> | ||
| 8 | #include <functional> | ||
| 9 | #include <mutex> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "core/memory.h" | ||
| 14 | |||
| 15 | namespace Core { | ||
| 16 | |||
| 17 | class GPUDirtyMemoryManager { | ||
| 18 | public: | ||
| 19 | GPUDirtyMemoryManager() : current{default_transform} { | ||
| 20 | back_buffer.reserve(256); | ||
| 21 | front_buffer.reserve(256); | ||
| 22 | } | ||
| 23 | |||
| 24 | ~GPUDirtyMemoryManager() = default; | ||
| 25 | |||
| 26 | void Collect(VAddr address, size_t size) { | ||
| 27 | TransformAddress t = BuildTransform(address, size); | ||
| 28 | TransformAddress tmp, original; | ||
| 29 | do { | ||
| 30 | tmp = current.load(std::memory_order_acquire); | ||
| 31 | original = tmp; | ||
| 32 | if (tmp.address != t.address) { | ||
| 33 | if (IsValid(tmp.address)) { | ||
| 34 | std::scoped_lock lk(guard); | ||
| 35 | back_buffer.emplace_back(tmp); | ||
| 36 | current.exchange(t, std::memory_order_relaxed); | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | tmp.address = t.address; | ||
| 40 | tmp.mask = 0; | ||
| 41 | } | ||
| 42 | if ((tmp.mask | t.mask) == tmp.mask) { | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | tmp.mask |= t.mask; | ||
| 46 | } while (!current.compare_exchange_weak(original, tmp, std::memory_order_release, | ||
| 47 | std::memory_order_relaxed)); | ||
| 48 | } | ||
| 49 | |||
| 50 | void Gather(std::function<void(VAddr, size_t)>& callback) { | ||
| 51 | { | ||
| 52 | std::scoped_lock lk(guard); | ||
| 53 | TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed); | ||
| 54 | front_buffer.swap(back_buffer); | ||
| 55 | if (IsValid(t.address)) { | ||
| 56 | front_buffer.emplace_back(t); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | for (auto& transform : front_buffer) { | ||
| 60 | size_t offset = 0; | ||
| 61 | u64 mask = transform.mask; | ||
| 62 | while (mask != 0) { | ||
| 63 | const size_t empty_bits = std::countr_zero(mask); | ||
| 64 | offset += empty_bits << align_bits; | ||
| 65 | mask = mask >> empty_bits; | ||
| 66 | |||
| 67 | const size_t continuous_bits = std::countr_one(mask); | ||
| 68 | callback((static_cast<VAddr>(transform.address) << page_bits) + offset, | ||
| 69 | continuous_bits << align_bits); | ||
| 70 | mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0; | ||
| 71 | offset += continuous_bits << align_bits; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | front_buffer.clear(); | ||
| 75 | } | ||
| 76 | |||
| 77 | private: | ||
| 78 | struct alignas(8) TransformAddress { | ||
| 79 | u32 address; | ||
| 80 | u32 mask; | ||
| 81 | }; | ||
| 82 | |||
| 83 | constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1; | ||
| 84 | constexpr static size_t page_size = 1ULL << page_bits; | ||
| 85 | constexpr static size_t page_mask = page_size - 1; | ||
| 86 | |||
| 87 | constexpr static size_t align_bits = 6U; | ||
| 88 | constexpr static size_t align_size = 1U << align_bits; | ||
| 89 | constexpr static size_t align_mask = align_size - 1; | ||
| 90 | constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U}; | ||
| 91 | |||
| 92 | bool IsValid(VAddr address) { | ||
| 93 | return address < (1ULL << 39); | ||
| 94 | } | ||
| 95 | |||
| 96 | template <typename T> | ||
| 97 | T CreateMask(size_t top_bit, size_t minor_bit) { | ||
| 98 | T mask = ~T(0); | ||
| 99 | mask <<= (sizeof(T) * 8 - top_bit); | ||
| 100 | mask >>= (sizeof(T) * 8 - top_bit); | ||
| 101 | mask >>= minor_bit; | ||
| 102 | mask <<= minor_bit; | ||
| 103 | return mask; | ||
| 104 | } | ||
| 105 | |||
| 106 | TransformAddress BuildTransform(VAddr address, size_t size) { | ||
| 107 | const size_t minor_address = address & page_mask; | ||
| 108 | const size_t minor_bit = minor_address >> align_bits; | ||
| 109 | const size_t top_bit = (minor_address + size + align_mask) >> align_bits; | ||
| 110 | TransformAddress result{}; | ||
| 111 | result.address = static_cast<u32>(address >> page_bits); | ||
| 112 | result.mask = CreateMask<u32>(top_bit, minor_bit); | ||
| 113 | return result; | ||
| 114 | } | ||
| 115 | |||
| 116 | std::atomic<TransformAddress> current{}; | ||
| 117 | std::mutex guard; | ||
| 118 | std::vector<TransformAddress> back_buffer; | ||
| 119 | std::vector<TransformAddress> front_buffer; | ||
| 120 | }; | ||
| 121 | |||
| 122 | } // namespace Core | ||
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 1ebc32c1e..94bd656fe 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -1243,10 +1243,12 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( | |||
| 1243 | auto& nfc_output_device = output_devices[3]; | 1243 | auto& nfc_output_device = output_devices[3]; |
| 1244 | 1244 | ||
| 1245 | if (device_index == EmulatedDeviceIndex::LeftIndex) { | 1245 | if (device_index == EmulatedDeviceIndex::LeftIndex) { |
| 1246 | controller.left_polling_mode = polling_mode; | ||
| 1246 | return left_output_device->SetPollingMode(polling_mode); | 1247 | return left_output_device->SetPollingMode(polling_mode); |
| 1247 | } | 1248 | } |
| 1248 | 1249 | ||
| 1249 | if (device_index == EmulatedDeviceIndex::RightIndex) { | 1250 | if (device_index == EmulatedDeviceIndex::RightIndex) { |
| 1251 | controller.right_polling_mode = polling_mode; | ||
| 1250 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); | 1252 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); |
| 1251 | const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); | 1253 | const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); |
| 1252 | 1254 | ||
| @@ -1261,12 +1263,22 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( | |||
| 1261 | return mapped_nfc_result; | 1263 | return mapped_nfc_result; |
| 1262 | } | 1264 | } |
| 1263 | 1265 | ||
| 1266 | controller.left_polling_mode = polling_mode; | ||
| 1267 | controller.right_polling_mode = polling_mode; | ||
| 1264 | left_output_device->SetPollingMode(polling_mode); | 1268 | left_output_device->SetPollingMode(polling_mode); |
| 1265 | right_output_device->SetPollingMode(polling_mode); | 1269 | right_output_device->SetPollingMode(polling_mode); |
| 1266 | nfc_output_device->SetPollingMode(polling_mode); | 1270 | nfc_output_device->SetPollingMode(polling_mode); |
| 1267 | return Common::Input::DriverResult::Success; | 1271 | return Common::Input::DriverResult::Success; |
| 1268 | } | 1272 | } |
| 1269 | 1273 | ||
| 1274 | Common::Input::PollingMode EmulatedController::GetPollingMode( | ||
| 1275 | EmulatedDeviceIndex device_index) const { | ||
| 1276 | if (device_index == EmulatedDeviceIndex::LeftIndex) { | ||
| 1277 | return controller.left_polling_mode; | ||
| 1278 | } | ||
| 1279 | return controller.right_polling_mode; | ||
| 1280 | } | ||
| 1281 | |||
| 1270 | bool EmulatedController::SetCameraFormat( | 1282 | bool EmulatedController::SetCameraFormat( |
| 1271 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { | 1283 | Core::IrSensor::ImageTransferProcessorFormat camera_format) { |
| 1272 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); | 1284 | LOG_INFO(Service_HID, "Set camera format {}", camera_format); |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d511e5fac..88d77db8d 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -143,6 +143,8 @@ struct ControllerStatus { | |||
| 143 | CameraState camera_state{}; | 143 | CameraState camera_state{}; |
| 144 | RingSensorForce ring_analog_state{}; | 144 | RingSensorForce ring_analog_state{}; |
| 145 | NfcState nfc_state{}; | 145 | NfcState nfc_state{}; |
| 146 | Common::Input::PollingMode left_polling_mode{}; | ||
| 147 | Common::Input::PollingMode right_polling_mode{}; | ||
| 146 | }; | 148 | }; |
| 147 | 149 | ||
| 148 | enum class ControllerTriggerType { | 150 | enum class ControllerTriggerType { |
| @@ -370,6 +372,12 @@ public: | |||
| 370 | */ | 372 | */ |
| 371 | Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index, | 373 | Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index, |
| 372 | Common::Input::PollingMode polling_mode); | 374 | Common::Input::PollingMode polling_mode); |
| 375 | /** | ||
| 376 | * Get the current polling mode from a controller | ||
| 377 | * @param device_index index of the controller to set the polling mode | ||
| 378 | * @return current polling mode | ||
| 379 | */ | ||
| 380 | Common::Input::PollingMode GetPollingMode(EmulatedDeviceIndex device_index) const; | ||
| 373 | 381 | ||
| 374 | /** | 382 | /** |
| 375 | * Sets the desired camera format to be polled from a controller | 383 | * Sets the desired camera format to be polled from a controller |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dd662b3f8..d178c2453 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -338,6 +338,15 @@ public: | |||
| 338 | return m_parent != nullptr; | 338 | return m_parent != nullptr; |
| 339 | } | 339 | } |
| 340 | 340 | ||
| 341 | std::span<KSynchronizationObject*> GetSynchronizationObjectBuffer() { | ||
| 342 | return m_sync_object_buffer.sync_objects; | ||
| 343 | } | ||
| 344 | |||
| 345 | std::span<Handle> GetHandleBuffer() { | ||
| 346 | return {m_sync_object_buffer.handles.data() + Svc::ArgumentHandleCountMax, | ||
| 347 | Svc::ArgumentHandleCountMax}; | ||
| 348 | } | ||
| 349 | |||
| 341 | u16 GetUserDisableCount() const; | 350 | u16 GetUserDisableCount() const; |
| 342 | void SetInterruptFlag(); | 351 | void SetInterruptFlag(); |
| 343 | void ClearInterruptFlag(); | 352 | void ClearInterruptFlag(); |
| @@ -855,6 +864,7 @@ private: | |||
| 855 | u32* m_light_ipc_data{}; | 864 | u32* m_light_ipc_data{}; |
| 856 | KProcessAddress m_tls_address{}; | 865 | KProcessAddress m_tls_address{}; |
| 857 | KLightLock m_activity_pause_lock; | 866 | KLightLock m_activity_pause_lock; |
| 867 | SyncObjectBuffer m_sync_object_buffer{}; | ||
| 858 | s64 m_schedule_count{}; | 868 | s64 m_schedule_count{}; |
| 859 | s64 m_last_scheduled_tick{}; | 869 | s64 m_last_scheduled_tick{}; |
| 860 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> m_per_core_priority_queue_entry{}; | 870 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> m_per_core_priority_queue_entry{}; |
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index 60247df2e..bb94f6934 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp | |||
| @@ -38,22 +38,31 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha | |||
| 38 | 38 | ||
| 39 | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, | 39 | Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, |
| 40 | Handle reply_target, s64 timeout_ns) { | 40 | Handle reply_target, s64 timeout_ns) { |
| 41 | // Ensure number of handles is valid. | ||
| 42 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | ||
| 43 | |||
| 44 | // Get the synchronization context. | ||
| 41 | auto& kernel = system.Kernel(); | 45 | auto& kernel = system.Kernel(); |
| 42 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); | 46 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); |
| 43 | 47 | auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); | |
| 44 | R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); | 48 | auto handles = GetCurrentThread(kernel).GetHandleBuffer(); |
| 45 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( | 49 | |
| 46 | handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), | 50 | // Copy user handles. |
| 47 | ResultInvalidPointer); | 51 | if (num_handles > 0) { |
| 48 | 52 | // Ensure we can try to get the handles. | |
| 49 | std::array<Handle, Svc::ArgumentHandleCountMax> handles; | 53 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( |
| 50 | GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles); | 54 | handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)), |
| 51 | 55 | ResultInvalidPointer); | |
| 52 | // Convert handle list to object table. | 56 | |
| 53 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; | 57 | // Get the handles. |
| 54 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(), | 58 | GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), |
| 55 | num_handles), | 59 | sizeof(Handle) * num_handles); |
| 56 | ResultInvalidHandle); | 60 | |
| 61 | // Convert the handles to objects. | ||
| 62 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( | ||
| 63 | objs.data(), handles.data(), num_handles), | ||
| 64 | ResultInvalidHandle); | ||
| 65 | } | ||
| 57 | 66 | ||
| 58 | // Ensure handles are closed when we're done. | 67 | // Ensure handles are closed when we're done. |
| 59 | SCOPE_EXIT({ | 68 | SCOPE_EXIT({ |
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 53df5bcd8..f02d03f30 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp | |||
| @@ -47,21 +47,35 @@ Result ResetSignal(Core::System& system, Handle handle) { | |||
| 47 | R_THROW(ResultInvalidHandle); | 47 | R_THROW(ResultInvalidHandle); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | static Result WaitSynchronization(Core::System& system, int32_t* out_index, const Handle* handles, | 50 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 51 | int32_t num_handles, int64_t timeout_ns) { | 51 | Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_handles, |
| 52 | int32_t num_handles, int64_t timeout_ns) { | ||
| 53 | LOG_TRACE(Kernel_SVC, "called user_handles={:#x}, num_handles={}, timeout_ns={}", user_handles, | ||
| 54 | num_handles, timeout_ns); | ||
| 55 | |||
| 52 | // Ensure number of handles is valid. | 56 | // Ensure number of handles is valid. |
| 53 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); | 57 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); |
| 54 | 58 | ||
| 55 | // Get the synchronization context. | 59 | // Get the synchronization context. |
| 56 | auto& kernel = system.Kernel(); | 60 | auto& kernel = system.Kernel(); |
| 57 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); | 61 | auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); |
| 58 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs; | 62 | auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); |
| 63 | auto handles = GetCurrentThread(kernel).GetHandleBuffer(); | ||
| 59 | 64 | ||
| 60 | // Copy user handles. | 65 | // Copy user handles. |
| 61 | if (num_handles > 0) { | 66 | if (num_handles > 0) { |
| 67 | // Ensure we can try to get the handles. | ||
| 68 | R_UNLESS(GetCurrentMemory(kernel).IsValidVirtualAddressRange( | ||
| 69 | user_handles, static_cast<u64>(sizeof(Handle) * num_handles)), | ||
| 70 | ResultInvalidPointer); | ||
| 71 | |||
| 72 | // Get the handles. | ||
| 73 | GetCurrentMemory(kernel).ReadBlock(user_handles, handles.data(), | ||
| 74 | sizeof(Handle) * num_handles); | ||
| 75 | |||
| 62 | // Convert the handles to objects. | 76 | // Convert the handles to objects. |
| 63 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, | 77 | R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( |
| 64 | num_handles), | 78 | objs.data(), handles.data(), num_handles), |
| 65 | ResultInvalidHandle); | 79 | ResultInvalidHandle); |
| 66 | } | 80 | } |
| 67 | 81 | ||
| @@ -80,23 +94,6 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons | |||
| 80 | R_RETURN(res); | 94 | R_RETURN(res); |
| 81 | } | 95 | } |
| 82 | 96 | ||
| 83 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 84 | Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_handles, | ||
| 85 | int32_t num_handles, int64_t timeout_ns) { | ||
| 86 | LOG_TRACE(Kernel_SVC, "called user_handles={:#x}, num_handles={}, timeout_ns={}", user_handles, | ||
| 87 | num_handles, timeout_ns); | ||
| 88 | |||
| 89 | // Ensure number of handles is valid. | ||
| 90 | R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); | ||
| 91 | std::array<Handle, Svc::ArgumentHandleCountMax> handles; | ||
| 92 | if (num_handles > 0) { | ||
| 93 | GetCurrentMemory(system.Kernel()) | ||
| 94 | .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); | ||
| 95 | } | ||
| 96 | |||
| 97 | R_RETURN(WaitSynchronization(system, out_index, handles.data(), num_handles, timeout_ns)); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Resumes a thread waiting on WaitSynchronization | 97 | /// Resumes a thread waiting on WaitSynchronization |
| 101 | Result CancelSynchronization(Core::System& system, Handle handle) { | 98 | Result CancelSynchronization(Core::System& system, Handle handle) { |
| 102 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); | 99 | LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); |
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 5bf289818..2d633b03f 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -66,10 +66,6 @@ NfcDevice::~NfcDevice() { | |||
| 66 | }; | 66 | }; |
| 67 | 67 | ||
| 68 | void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { | 68 | void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { |
| 69 | if (!is_initalized) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | if (type == Core::HID::ControllerTriggerType::Connected) { | 69 | if (type == Core::HID::ControllerTriggerType::Connected) { |
| 74 | Initialize(); | 70 | Initialize(); |
| 75 | availability_change_event->Signal(); | 71 | availability_change_event->Signal(); |
| @@ -77,12 +73,12 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { | |||
| 77 | } | 73 | } |
| 78 | 74 | ||
| 79 | if (type == Core::HID::ControllerTriggerType::Disconnected) { | 75 | if (type == Core::HID::ControllerTriggerType::Disconnected) { |
| 80 | device_state = DeviceState::Unavailable; | 76 | Finalize(); |
| 81 | availability_change_event->Signal(); | 77 | availability_change_event->Signal(); |
| 82 | return; | 78 | return; |
| 83 | } | 79 | } |
| 84 | 80 | ||
| 85 | if (type != Core::HID::ControllerTriggerType::Nfc) { | 81 | if (!is_initalized) { |
| 86 | return; | 82 | return; |
| 87 | } | 83 | } |
| 88 | 84 | ||
| @@ -90,6 +86,17 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { | |||
| 90 | return; | 86 | return; |
| 91 | } | 87 | } |
| 92 | 88 | ||
| 89 | // Ensure nfc mode is always active | ||
| 90 | if (npad_device->GetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex) == | ||
| 91 | Common::Input::PollingMode::Active) { | ||
| 92 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 93 | Common::Input::PollingMode::NFC); | ||
| 94 | } | ||
| 95 | |||
| 96 | if (type != Core::HID::ControllerTriggerType::Nfc) { | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 93 | const auto nfc_status = npad_device->GetNfc(); | 100 | const auto nfc_status = npad_device->GetNfc(); |
| 94 | switch (nfc_status.state) { | 101 | switch (nfc_status.state) { |
| 95 | case Common::Input::NfcState::NewAmiibo: | 102 | case Common::Input::NfcState::NewAmiibo: |
| @@ -207,11 +214,14 @@ void NfcDevice::Initialize() { | |||
| 207 | } | 214 | } |
| 208 | 215 | ||
| 209 | void NfcDevice::Finalize() { | 216 | void NfcDevice::Finalize() { |
| 210 | if (device_state == DeviceState::TagMounted) { | 217 | if (npad_device->IsConnected()) { |
| 211 | Unmount(); | 218 | if (device_state == DeviceState::TagMounted) { |
| 212 | } | 219 | Unmount(); |
| 213 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | 220 | } |
| 214 | StopDetection(); | 221 | if (device_state == DeviceState::SearchingForTag || |
| 222 | device_state == DeviceState::TagRemoved) { | ||
| 223 | StopDetection(); | ||
| 224 | } | ||
| 215 | } | 225 | } |
| 216 | 226 | ||
| 217 | if (device_state != DeviceState::Unavailable) { | 227 | if (device_state != DeviceState::Unavailable) { |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 514ba0d66..257406f09 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <span> | ||
| 6 | 7 | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "common/atomic_ops.h" | 9 | #include "common/atomic_ops.h" |
| @@ -13,6 +14,7 @@ | |||
| 13 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 14 | #include "core/core.h" | 15 | #include "core/core.h" |
| 15 | #include "core/device_memory.h" | 16 | #include "core/device_memory.h" |
| 17 | #include "core/gpu_dirty_memory_manager.h" | ||
| 16 | #include "core/hardware_properties.h" | 18 | #include "core/hardware_properties.h" |
| 17 | #include "core/hle/kernel/k_page_table.h" | 19 | #include "core/hle/kernel/k_page_table.h" |
| 18 | #include "core/hle/kernel/k_process.h" | 20 | #include "core/hle/kernel/k_process.h" |
| @@ -678,7 +680,7 @@ struct Memory::Impl { | |||
| 678 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, | 680 | LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, |
| 679 | GetInteger(vaddr), static_cast<u64>(data)); | 681 | GetInteger(vaddr), static_cast<u64>(data)); |
| 680 | }, | 682 | }, |
| 681 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); }); | 683 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); |
| 682 | if (ptr) { | 684 | if (ptr) { |
| 683 | std::memcpy(ptr, &data, sizeof(T)); | 685 | std::memcpy(ptr, &data, sizeof(T)); |
| 684 | } | 686 | } |
| @@ -692,7 +694,7 @@ struct Memory::Impl { | |||
| 692 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", | 694 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}", |
| 693 | sizeof(T) * 8, GetInteger(vaddr), static_cast<u64>(data)); | 695 | sizeof(T) * 8, GetInteger(vaddr), static_cast<u64>(data)); |
| 694 | }, | 696 | }, |
| 695 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(T)); }); | 697 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(T)); }); |
| 696 | if (ptr) { | 698 | if (ptr) { |
| 697 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); | 699 | const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr); |
| 698 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 700 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| @@ -707,7 +709,7 @@ struct Memory::Impl { | |||
| 707 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", | 709 | LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}", |
| 708 | GetInteger(vaddr), static_cast<u64>(data[1]), static_cast<u64>(data[0])); | 710 | GetInteger(vaddr), static_cast<u64>(data[1]), static_cast<u64>(data[0])); |
| 709 | }, | 711 | }, |
| 710 | [&]() { system.GPU().InvalidateRegion(GetInteger(vaddr), sizeof(u128)); }); | 712 | [&]() { HandleRasterizerWrite(GetInteger(vaddr), sizeof(u128)); }); |
| 711 | if (ptr) { | 713 | if (ptr) { |
| 712 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); | 714 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr); |
| 713 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); | 715 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| @@ -717,7 +719,7 @@ struct Memory::Impl { | |||
| 717 | 719 | ||
| 718 | void HandleRasterizerDownload(VAddr address, size_t size) { | 720 | void HandleRasterizerDownload(VAddr address, size_t size) { |
| 719 | const size_t core = system.GetCurrentHostThreadID(); | 721 | const size_t core = system.GetCurrentHostThreadID(); |
| 720 | auto& current_area = rasterizer_areas[core]; | 722 | auto& current_area = rasterizer_read_areas[core]; |
| 721 | const VAddr end_address = address + size; | 723 | const VAddr end_address = address + size; |
| 722 | if (current_area.start_address <= address && end_address <= current_area.end_address) | 724 | if (current_area.start_address <= address && end_address <= current_area.end_address) |
| 723 | [[likely]] { | 725 | [[likely]] { |
| @@ -726,9 +728,31 @@ struct Memory::Impl { | |||
| 726 | current_area = system.GPU().OnCPURead(address, size); | 728 | current_area = system.GPU().OnCPURead(address, size); |
| 727 | } | 729 | } |
| 728 | 730 | ||
| 729 | Common::PageTable* current_page_table = nullptr; | 731 | void HandleRasterizerWrite(VAddr address, size_t size) { |
| 730 | std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{}; | 732 | const size_t core = system.GetCurrentHostThreadID(); |
| 733 | auto& current_area = rasterizer_write_areas[core]; | ||
| 734 | VAddr subaddress = address >> YUZU_PAGEBITS; | ||
| 735 | bool do_collection = current_area.last_address == subaddress; | ||
| 736 | if (!do_collection) [[unlikely]] { | ||
| 737 | do_collection = system.GPU().OnCPUWrite(address, size); | ||
| 738 | if (!do_collection) { | ||
| 739 | return; | ||
| 740 | } | ||
| 741 | current_area.last_address = subaddress; | ||
| 742 | } | ||
| 743 | gpu_dirty_managers[core].Collect(address, size); | ||
| 744 | } | ||
| 745 | |||
| 746 | struct GPUDirtyState { | ||
| 747 | VAddr last_address; | ||
| 748 | }; | ||
| 749 | |||
| 731 | Core::System& system; | 750 | Core::System& system; |
| 751 | Common::PageTable* current_page_table = nullptr; | ||
| 752 | std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> | ||
| 753 | rasterizer_read_areas{}; | ||
| 754 | std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; | ||
| 755 | std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; | ||
| 732 | }; | 756 | }; |
| 733 | 757 | ||
| 734 | Memory::Memory(Core::System& system_) : system{system_} { | 758 | Memory::Memory(Core::System& system_) : system{system_} { |
| @@ -876,6 +900,10 @@ void Memory::ZeroBlock(Common::ProcessAddress dest_addr, const std::size_t size) | |||
| 876 | impl->ZeroBlock(*system.ApplicationProcess(), dest_addr, size); | 900 | impl->ZeroBlock(*system.ApplicationProcess(), dest_addr, size); |
| 877 | } | 901 | } |
| 878 | 902 | ||
| 903 | void Memory::SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers) { | ||
| 904 | impl->gpu_dirty_managers = managers; | ||
| 905 | } | ||
| 906 | |||
| 879 | Result Memory::InvalidateDataCache(Common::ProcessAddress dest_addr, const std::size_t size) { | 907 | Result Memory::InvalidateDataCache(Common::ProcessAddress dest_addr, const std::size_t size) { |
| 880 | return impl->InvalidateDataCache(*system.ApplicationProcess(), dest_addr, size); | 908 | return impl->InvalidateDataCache(*system.ApplicationProcess(), dest_addr, size); |
| 881 | } | 909 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 72a0be813..ea01824f8 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <span> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include "common/typed_address.h" | 10 | #include "common/typed_address.h" |
| 10 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| @@ -15,7 +16,8 @@ struct PageTable; | |||
| 15 | 16 | ||
| 16 | namespace Core { | 17 | namespace Core { |
| 17 | class System; | 18 | class System; |
| 18 | } | 19 | class GPUDirtyMemoryManager; |
| 20 | } // namespace Core | ||
| 19 | 21 | ||
| 20 | namespace Kernel { | 22 | namespace Kernel { |
| 21 | class PhysicalMemory; | 23 | class PhysicalMemory; |
| @@ -458,6 +460,8 @@ public: | |||
| 458 | */ | 460 | */ |
| 459 | void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); | 461 | void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); |
| 460 | 462 | ||
| 463 | void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); | ||
| 464 | |||
| 461 | private: | 465 | private: |
| 462 | Core::System& system; | 466 | Core::System& system; |
| 463 | 467 | ||
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 58a45ab67..b5ed3380f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -115,7 +115,34 @@ void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) { | |||
| 115 | 115 | ||
| 116 | template <class P> | 116 | template <class P> |
| 117 | void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { | 117 | void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) { |
| 118 | memory_tracker.CachedCpuWrite(cpu_addr, size); | 118 | const bool is_dirty = IsRegionRegistered(cpu_addr, size); |
| 119 | if (!is_dirty) { | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | VAddr aligned_start = Common::AlignDown(cpu_addr, YUZU_PAGESIZE); | ||
| 123 | VAddr aligned_end = Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE); | ||
| 124 | if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) { | ||
| 125 | WriteMemory(cpu_addr, size); | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | |||
| 129 | tmp_buffer.resize_destructive(size); | ||
| 130 | cpu_memory.ReadBlockUnsafe(cpu_addr, tmp_buffer.data(), size); | ||
| 131 | |||
| 132 | InlineMemoryImplementation(cpu_addr, size, tmp_buffer); | ||
| 133 | } | ||
| 134 | |||
| 135 | template <class P> | ||
| 136 | bool BufferCache<P>::OnCPUWrite(VAddr cpu_addr, u64 size) { | ||
| 137 | const bool is_dirty = IsRegionRegistered(cpu_addr, size); | ||
| 138 | if (!is_dirty) { | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) { | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | WriteMemory(cpu_addr, size); | ||
| 145 | return false; | ||
| 119 | } | 146 | } |
| 120 | 147 | ||
| 121 | template <class P> | 148 | template <class P> |
| @@ -1553,6 +1580,14 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, | |||
| 1553 | return false; | 1580 | return false; |
| 1554 | } | 1581 | } |
| 1555 | 1582 | ||
| 1583 | InlineMemoryImplementation(dest_address, copy_size, inlined_buffer); | ||
| 1584 | |||
| 1585 | return true; | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | template <class P> | ||
| 1589 | void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_size, | ||
| 1590 | std::span<const u8> inlined_buffer) { | ||
| 1556 | const IntervalType subtract_interval{dest_address, dest_address + copy_size}; | 1591 | const IntervalType subtract_interval{dest_address, dest_address + copy_size}; |
| 1557 | ClearDownload(subtract_interval); | 1592 | ClearDownload(subtract_interval); |
| 1558 | common_ranges.subtract(subtract_interval); | 1593 | common_ranges.subtract(subtract_interval); |
| @@ -1574,8 +1609,6 @@ bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, | |||
| 1574 | } else { | 1609 | } else { |
| 1575 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); | 1610 | buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); |
| 1576 | } | 1611 | } |
| 1577 | |||
| 1578 | return true; | ||
| 1579 | } | 1612 | } |
| 1580 | 1613 | ||
| 1581 | template <class P> | 1614 | template <class P> |
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index fe6068cfe..460fc7551 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h | |||
| @@ -245,6 +245,8 @@ public: | |||
| 245 | 245 | ||
| 246 | void CachedWriteMemory(VAddr cpu_addr, u64 size); | 246 | void CachedWriteMemory(VAddr cpu_addr, u64 size); |
| 247 | 247 | ||
| 248 | bool OnCPUWrite(VAddr cpu_addr, u64 size); | ||
| 249 | |||
| 248 | void DownloadMemory(VAddr cpu_addr, u64 size); | 250 | void DownloadMemory(VAddr cpu_addr, u64 size); |
| 249 | 251 | ||
| 250 | std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size); | 252 | std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size); |
| @@ -543,6 +545,9 @@ private: | |||
| 543 | 545 | ||
| 544 | void ClearDownload(IntervalType subtract_interval); | 546 | void ClearDownload(IntervalType subtract_interval); |
| 545 | 547 | ||
| 548 | void InlineMemoryImplementation(VAddr dest_address, size_t copy_size, | ||
| 549 | std::span<const u8> inlined_buffer); | ||
| 550 | |||
| 546 | VideoCore::RasterizerInterface& rasterizer; | 551 | VideoCore::RasterizerInterface& rasterizer; |
| 547 | Core::Memory::Memory& cpu_memory; | 552 | Core::Memory::Memory& cpu_memory; |
| 548 | 553 | ||
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index ab4f4d407..87d69ebc5 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp | |||
| @@ -272,6 +272,9 @@ constexpr Table MakeNonNativeBgrCopyTable() { | |||
| 272 | 272 | ||
| 273 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, | 273 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views, |
| 274 | bool native_bgr) { | 274 | bool native_bgr) { |
| 275 | if (format_a == format_b) { | ||
| 276 | return true; | ||
| 277 | } | ||
| 275 | if (broken_views) { | 278 | if (broken_views) { |
| 276 | // If format views are broken, only accept formats that are identical. | 279 | // If format views are broken, only accept formats that are identical. |
| 277 | return format_a == format_b; | 280 | return format_a == format_b; |
| @@ -282,6 +285,9 @@ bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_vi | |||
| 282 | } | 285 | } |
| 283 | 286 | ||
| 284 | bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) { | 287 | bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) { |
| 288 | if (format_a == format_b) { | ||
| 289 | return true; | ||
| 290 | } | ||
| 285 | static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable(); | 291 | static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable(); |
| 286 | static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable(); | 292 | static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable(); |
| 287 | return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b); | 293 | return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b); |
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 35d699bbf..ab20ff30f 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h | |||
| @@ -69,7 +69,6 @@ public: | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | void SignalFence(std::function<void()>&& func) { | 71 | void SignalFence(std::function<void()>&& func) { |
| 72 | rasterizer.InvalidateGPUCache(); | ||
| 73 | bool delay_fence = Settings::IsGPULevelHigh(); | 72 | bool delay_fence = Settings::IsGPULevelHigh(); |
| 74 | if constexpr (!can_async_check) { | 73 | if constexpr (!can_async_check) { |
| 75 | TryReleasePendingFences<false>(); | 74 | TryReleasePendingFences<false>(); |
| @@ -96,6 +95,7 @@ public: | |||
| 96 | guard.unlock(); | 95 | guard.unlock(); |
| 97 | cv.notify_all(); | 96 | cv.notify_all(); |
| 98 | } | 97 | } |
| 98 | rasterizer.InvalidateGPUCache(); | ||
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | void SignalSyncPoint(u32 value) { | 101 | void SignalSyncPoint(u32 value) { |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index db385076d..c192e33b2 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -95,7 +95,9 @@ struct GPU::Impl { | |||
| 95 | 95 | ||
| 96 | /// Synchronizes CPU writes with Host GPU memory. | 96 | /// Synchronizes CPU writes with Host GPU memory. |
| 97 | void InvalidateGPUCache() { | 97 | void InvalidateGPUCache() { |
| 98 | rasterizer->InvalidateGPUCache(); | 98 | std::function<void(VAddr, size_t)> callback_writes( |
| 99 | [this](VAddr address, size_t size) { rasterizer->OnCacheInvalidation(address, size); }); | ||
| 100 | system.GatherGPUDirtyMemory(callback_writes); | ||
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | /// Signal the ending of command list. | 103 | /// Signal the ending of command list. |
| @@ -299,6 +301,10 @@ struct GPU::Impl { | |||
| 299 | gpu_thread.InvalidateRegion(addr, size); | 301 | gpu_thread.InvalidateRegion(addr, size); |
| 300 | } | 302 | } |
| 301 | 303 | ||
| 304 | bool OnCPUWrite(VAddr addr, u64 size) { | ||
| 305 | return rasterizer->OnCPUWrite(addr, size); | ||
| 306 | } | ||
| 307 | |||
| 302 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 308 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 303 | void FlushAndInvalidateRegion(VAddr addr, u64 size) { | 309 | void FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 304 | gpu_thread.FlushAndInvalidateRegion(addr, size); | 310 | gpu_thread.FlushAndInvalidateRegion(addr, size); |
| @@ -561,6 +567,10 @@ void GPU::InvalidateRegion(VAddr addr, u64 size) { | |||
| 561 | impl->InvalidateRegion(addr, size); | 567 | impl->InvalidateRegion(addr, size); |
| 562 | } | 568 | } |
| 563 | 569 | ||
| 570 | bool GPU::OnCPUWrite(VAddr addr, u64 size) { | ||
| 571 | return impl->OnCPUWrite(addr, size); | ||
| 572 | } | ||
| 573 | |||
| 564 | void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 574 | void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 565 | impl->FlushAndInvalidateRegion(addr, size); | 575 | impl->FlushAndInvalidateRegion(addr, size); |
| 566 | } | 576 | } |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index e49c40cf2..ba2838b89 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -250,6 +250,10 @@ public: | |||
| 250 | /// Notify rasterizer that any caches of the specified region should be invalidated | 250 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 251 | void InvalidateRegion(VAddr addr, u64 size); | 251 | void InvalidateRegion(VAddr addr, u64 size); |
| 252 | 252 | ||
| 253 | /// Notify rasterizer that CPU is trying to write this area. It returns true if the area is | ||
| 254 | /// sensible, false otherwise | ||
| 255 | bool OnCPUWrite(VAddr addr, u64 size); | ||
| 256 | |||
| 253 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 257 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 254 | void FlushAndInvalidateRegion(VAddr addr, u64 size); | 258 | void FlushAndInvalidateRegion(VAddr addr, u64 size); |
| 255 | 259 | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 889144f38..2f0f9f593 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -47,7 +47,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||
| 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { | 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { |
| 48 | rasterizer->FlushRegion(flush->addr, flush->size); | 48 | rasterizer->FlushRegion(flush->addr, flush->size); |
| 49 | } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { | 49 | } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { |
| 50 | rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); | 50 | rasterizer->OnCacheInvalidation(invalidate->addr, invalidate->size); |
| 51 | } else { | 51 | } else { |
| 52 | ASSERT(false); | 52 | ASSERT(false); |
| 53 | } | 53 | } |
| @@ -102,12 +102,12 @@ void ThreadManager::TickGPU() { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { | 104 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { |
| 105 | rasterizer->OnCPUWrite(addr, size); | 105 | rasterizer->OnCacheInvalidation(addr, size); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 108 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 109 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important | 109 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important |
| 110 | rasterizer->OnCPUWrite(addr, size); | 110 | rasterizer->OnCacheInvalidation(addr, size); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { | 113 | u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 7566a8c4e..cb8029a4f 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -109,7 +109,9 @@ public: | |||
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | /// Notify rasterizer that any caches of the specified region are desync with guest | 111 | /// Notify rasterizer that any caches of the specified region are desync with guest |
| 112 | virtual void OnCPUWrite(VAddr addr, u64 size) = 0; | 112 | virtual void OnCacheInvalidation(VAddr addr, u64 size) = 0; |
| 113 | |||
| 114 | virtual bool OnCPUWrite(VAddr addr, u64 size) = 0; | ||
| 113 | 115 | ||
| 114 | /// Sync memory between guest and host. | 116 | /// Sync memory between guest and host. |
| 115 | virtual void InvalidateGPUCache() = 0; | 117 | virtual void InvalidateGPUCache() = 0; |
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index bf2ce4c49..92ecf6682 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp | |||
| @@ -47,7 +47,10 @@ bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size, VideoCommon::CacheTyp | |||
| 47 | return false; | 47 | return false; |
| 48 | } | 48 | } |
| 49 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} | 49 | void RasterizerNull::InvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} |
| 50 | void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} | 50 | bool RasterizerNull::OnCPUWrite(VAddr addr, u64 size) { |
| 51 | return false; | ||
| 52 | } | ||
| 53 | void RasterizerNull::OnCacheInvalidation(VAddr addr, u64 size) {} | ||
| 51 | VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) { | 54 | VideoCore::RasterizerDownloadArea RasterizerNull::GetFlushArea(VAddr addr, u64 size) { |
| 52 | VideoCore::RasterizerDownloadArea new_area{ | 55 | VideoCore::RasterizerDownloadArea new_area{ |
| 53 | .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE), | 56 | .start_address = Common::AlignDown(addr, Core::Memory::YUZU_PAGESIZE), |
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index a8d35d2c1..93b9a6971 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h | |||
| @@ -53,7 +53,8 @@ public: | |||
| 53 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 53 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 54 | void InvalidateRegion(VAddr addr, u64 size, | 54 | void InvalidateRegion(VAddr addr, u64 size, |
| 55 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 55 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 56 | void OnCPUWrite(VAddr addr, u64 size) override; | 56 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 57 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 57 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; | 58 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; |
| 58 | void InvalidateGPUCache() override; | 59 | void InvalidateGPUCache() override; |
| 59 | void UnmapMemory(VAddr addr, u64 size) override; | 60 | void UnmapMemory(VAddr addr, u64 size) override; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index edf527f2d..aadd6967c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -485,12 +485,33 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size, VideoCommon::Cache | |||
| 485 | } | 485 | } |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | 488 | bool RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { |
| 489 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 490 | if (addr == 0 || size == 0) { | ||
| 491 | return false; | ||
| 492 | } | ||
| 493 | |||
| 494 | { | ||
| 495 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 496 | if (buffer_cache.OnCPUWrite(addr, size)) { | ||
| 497 | return true; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | { | ||
| 502 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 503 | texture_cache.WriteMemory(addr, size); | ||
| 504 | } | ||
| 505 | |||
| 506 | shader_cache.InvalidateRegion(addr, size); | ||
| 507 | return false; | ||
| 508 | } | ||
| 509 | |||
| 510 | void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) { | ||
| 489 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 511 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 490 | if (addr == 0 || size == 0) { | 512 | if (addr == 0 || size == 0) { |
| 491 | return; | 513 | return; |
| 492 | } | 514 | } |
| 493 | shader_cache.OnCPUWrite(addr, size); | ||
| 494 | { | 515 | { |
| 495 | std::scoped_lock lock{texture_cache.mutex}; | 516 | std::scoped_lock lock{texture_cache.mutex}; |
| 496 | texture_cache.WriteMemory(addr, size); | 517 | texture_cache.WriteMemory(addr, size); |
| @@ -499,15 +520,11 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | |||
| 499 | std::scoped_lock lock{buffer_cache.mutex}; | 520 | std::scoped_lock lock{buffer_cache.mutex}; |
| 500 | buffer_cache.CachedWriteMemory(addr, size); | 521 | buffer_cache.CachedWriteMemory(addr, size); |
| 501 | } | 522 | } |
| 523 | shader_cache.InvalidateRegion(addr, size); | ||
| 502 | } | 524 | } |
| 503 | 525 | ||
| 504 | void RasterizerOpenGL::InvalidateGPUCache() { | 526 | void RasterizerOpenGL::InvalidateGPUCache() { |
| 505 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 527 | gpu.InvalidateGPUCache(); |
| 506 | shader_cache.SyncGuestHost(); | ||
| 507 | { | ||
| 508 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 509 | buffer_cache.FlushCachedWrites(); | ||
| 510 | } | ||
| 511 | } | 528 | } |
| 512 | 529 | ||
| 513 | void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { | 530 | void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { |
| @@ -519,7 +536,7 @@ void RasterizerOpenGL::UnmapMemory(VAddr addr, u64 size) { | |||
| 519 | std::scoped_lock lock{buffer_cache.mutex}; | 536 | std::scoped_lock lock{buffer_cache.mutex}; |
| 520 | buffer_cache.WriteMemory(addr, size); | 537 | buffer_cache.WriteMemory(addr, size); |
| 521 | } | 538 | } |
| 522 | shader_cache.OnCPUWrite(addr, size); | 539 | shader_cache.OnCacheInvalidation(addr, size); |
| 523 | } | 540 | } |
| 524 | 541 | ||
| 525 | void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { | 542 | void RasterizerOpenGL::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index a73ad15c1..8eda2ddba 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -98,7 +98,8 @@ public: | |||
| 98 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; | 98 | VideoCore::RasterizerDownloadArea GetFlushArea(VAddr addr, u64 size) override; |
| 99 | void InvalidateRegion(VAddr addr, u64 size, | 99 | void InvalidateRegion(VAddr addr, u64 size, |
| 100 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 100 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 101 | void OnCPUWrite(VAddr addr, u64 size) override; | 101 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 102 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 102 | void InvalidateGPUCache() override; | 103 | void InvalidateGPUCache() override; |
| 103 | void UnmapMemory(VAddr addr, u64 size) override; | 104 | void UnmapMemory(VAddr addr, u64 size) override; |
| 104 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; | 105 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f7c0d939a..456bb040e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -566,11 +566,32 @@ void RasterizerVulkan::InnerInvalidation(std::span<const std::pair<VAddr, std::s | |||
| 566 | } | 566 | } |
| 567 | } | 567 | } |
| 568 | 568 | ||
| 569 | void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | 569 | bool RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { |
| 570 | if (addr == 0 || size == 0) { | ||
| 571 | return false; | ||
| 572 | } | ||
| 573 | |||
| 574 | { | ||
| 575 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 576 | if (buffer_cache.OnCPUWrite(addr, size)) { | ||
| 577 | return true; | ||
| 578 | } | ||
| 579 | } | ||
| 580 | |||
| 581 | { | ||
| 582 | std::scoped_lock lock{texture_cache.mutex}; | ||
| 583 | texture_cache.WriteMemory(addr, size); | ||
| 584 | } | ||
| 585 | |||
| 586 | pipeline_cache.InvalidateRegion(addr, size); | ||
| 587 | return false; | ||
| 588 | } | ||
| 589 | |||
| 590 | void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) { | ||
| 570 | if (addr == 0 || size == 0) { | 591 | if (addr == 0 || size == 0) { |
| 571 | return; | 592 | return; |
| 572 | } | 593 | } |
| 573 | pipeline_cache.OnCPUWrite(addr, size); | 594 | |
| 574 | { | 595 | { |
| 575 | std::scoped_lock lock{texture_cache.mutex}; | 596 | std::scoped_lock lock{texture_cache.mutex}; |
| 576 | texture_cache.WriteMemory(addr, size); | 597 | texture_cache.WriteMemory(addr, size); |
| @@ -579,14 +600,11 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | |||
| 579 | std::scoped_lock lock{buffer_cache.mutex}; | 600 | std::scoped_lock lock{buffer_cache.mutex}; |
| 580 | buffer_cache.CachedWriteMemory(addr, size); | 601 | buffer_cache.CachedWriteMemory(addr, size); |
| 581 | } | 602 | } |
| 603 | pipeline_cache.InvalidateRegion(addr, size); | ||
| 582 | } | 604 | } |
| 583 | 605 | ||
| 584 | void RasterizerVulkan::InvalidateGPUCache() { | 606 | void RasterizerVulkan::InvalidateGPUCache() { |
| 585 | pipeline_cache.SyncGuestHost(); | 607 | gpu.InvalidateGPUCache(); |
| 586 | { | ||
| 587 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 588 | buffer_cache.FlushCachedWrites(); | ||
| 589 | } | ||
| 590 | } | 608 | } |
| 591 | 609 | ||
| 592 | void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { | 610 | void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { |
| @@ -598,7 +616,7 @@ void RasterizerVulkan::UnmapMemory(VAddr addr, u64 size) { | |||
| 598 | std::scoped_lock lock{buffer_cache.mutex}; | 616 | std::scoped_lock lock{buffer_cache.mutex}; |
| 599 | buffer_cache.WriteMemory(addr, size); | 617 | buffer_cache.WriteMemory(addr, size); |
| 600 | } | 618 | } |
| 601 | pipeline_cache.OnCPUWrite(addr, size); | 619 | pipeline_cache.OnCacheInvalidation(addr, size); |
| 602 | } | 620 | } |
| 603 | 621 | ||
| 604 | void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { | 622 | void RasterizerVulkan::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b39710b3c..73257d964 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -96,7 +96,8 @@ public: | |||
| 96 | void InvalidateRegion(VAddr addr, u64 size, | 96 | void InvalidateRegion(VAddr addr, u64 size, |
| 97 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; | 97 | VideoCommon::CacheType which = VideoCommon::CacheType::All) override; |
| 98 | void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; | 98 | void InnerInvalidation(std::span<const std::pair<VAddr, std::size_t>> sequences) override; |
| 99 | void OnCPUWrite(VAddr addr, u64 size) override; | 99 | void OnCacheInvalidation(VAddr addr, u64 size) override; |
| 100 | bool OnCPUWrite(VAddr addr, u64 size) override; | ||
| 100 | void InvalidateGPUCache() override; | 101 | void InvalidateGPUCache() override; |
| 101 | void UnmapMemory(VAddr addr, u64 size) override; | 102 | void UnmapMemory(VAddr addr, u64 size) override; |
| 102 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; | 103 | void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8385b5509..3aac3cfab 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -36,8 +36,10 @@ using VideoCommon::ImageFlagBits; | |||
| 36 | using VideoCommon::ImageInfo; | 36 | using VideoCommon::ImageInfo; |
| 37 | using VideoCommon::ImageType; | 37 | using VideoCommon::ImageType; |
| 38 | using VideoCommon::SubresourceRange; | 38 | using VideoCommon::SubresourceRange; |
| 39 | using VideoCore::Surface::BytesPerBlock; | ||
| 39 | using VideoCore::Surface::IsPixelFormatASTC; | 40 | using VideoCore::Surface::IsPixelFormatASTC; |
| 40 | using VideoCore::Surface::IsPixelFormatInteger; | 41 | using VideoCore::Surface::IsPixelFormatInteger; |
| 42 | using VideoCore::Surface::SurfaceType; | ||
| 41 | 43 | ||
| 42 | namespace { | 44 | namespace { |
| 43 | constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | 45 | constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { |
| @@ -130,7 +132,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 130 | [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { | 132 | [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { |
| 131 | const PixelFormat format = StorageFormat(info.format); | 133 | const PixelFormat format = StorageFormat(info.format); |
| 132 | const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format); | 134 | const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format); |
| 133 | VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; | 135 | VkImageCreateFlags flags{}; |
| 134 | if (info.type == ImageType::e2D && info.resources.layers >= 6 && | 136 | if (info.type == ImageType::e2D && info.resources.layers >= 6 && |
| 135 | info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) { | 137 | info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) { |
| 136 | flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; | 138 | flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; |
| @@ -163,11 +165,24 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 163 | } | 165 | } |
| 164 | 166 | ||
| 165 | [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, | 167 | [[nodiscard]] vk::Image MakeImage(const Device& device, const MemoryAllocator& allocator, |
| 166 | const ImageInfo& info) { | 168 | const ImageInfo& info, std::span<const VkFormat> view_formats) { |
| 167 | if (info.type == ImageType::Buffer) { | 169 | if (info.type == ImageType::Buffer) { |
| 168 | return vk::Image{}; | 170 | return vk::Image{}; |
| 169 | } | 171 | } |
| 170 | return allocator.CreateImage(MakeImageCreateInfo(device, info)); | 172 | VkImageCreateInfo image_ci = MakeImageCreateInfo(device, info); |
| 173 | const VkImageFormatListCreateInfo image_format_list = { | ||
| 174 | .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO, | ||
| 175 | .pNext = nullptr, | ||
| 176 | .viewFormatCount = static_cast<u32>(view_formats.size()), | ||
| 177 | .pViewFormats = view_formats.data(), | ||
| 178 | }; | ||
| 179 | if (view_formats.size() > 1) { | ||
| 180 | image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; | ||
| 181 | if (device.IsKhrImageFormatListSupported()) { | ||
| 182 | image_ci.pNext = &image_format_list; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | return allocator.CreateImage(image_ci); | ||
| 171 | } | 186 | } |
| 172 | 187 | ||
| 173 | [[nodiscard]] VkImageAspectFlags ImageAspectMask(PixelFormat format) { | 188 | [[nodiscard]] VkImageAspectFlags ImageAspectMask(PixelFormat format) { |
| @@ -806,6 +821,23 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched | |||
| 806 | astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, | 821 | astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, |
| 807 | compute_pass_descriptor_queue, memory_allocator); | 822 | compute_pass_descriptor_queue, memory_allocator); |
| 808 | } | 823 | } |
| 824 | if (!device.IsKhrImageFormatListSupported()) { | ||
| 825 | return; | ||
| 826 | } | ||
| 827 | for (size_t index_a = 0; index_a < VideoCore::Surface::MaxPixelFormat; index_a++) { | ||
| 828 | const auto image_format = static_cast<PixelFormat>(index_a); | ||
| 829 | if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) { | ||
| 830 | view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32); | ||
| 831 | } | ||
| 832 | for (size_t index_b = 0; index_b < VideoCore::Surface::MaxPixelFormat; index_b++) { | ||
| 833 | const auto view_format = static_cast<PixelFormat>(index_b); | ||
| 834 | if (VideoCore::Surface::IsViewCompatible(image_format, view_format, false, true)) { | ||
| 835 | const auto view_info = | ||
| 836 | MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, view_format); | ||
| 837 | view_formats[index_a].push_back(view_info.format); | ||
| 838 | } | ||
| 839 | } | ||
| 840 | } | ||
| 809 | } | 841 | } |
| 810 | 842 | ||
| 811 | void TextureCacheRuntime::Finish() { | 843 | void TextureCacheRuntime::Finish() { |
| @@ -1265,8 +1297,8 @@ void TextureCacheRuntime::TickFrame() {} | |||
| 1265 | Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, | 1297 | Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, |
| 1266 | VAddr cpu_addr_) | 1298 | VAddr cpu_addr_) |
| 1267 | : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, | 1299 | : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, |
| 1268 | runtime{&runtime_}, | 1300 | runtime{&runtime_}, original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, |
| 1269 | original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info)), | 1301 | runtime->ViewFormats(info.format))), |
| 1270 | aspect_mask(ImageAspectMask(info.format)) { | 1302 | aspect_mask(ImageAspectMask(info.format)) { |
| 1271 | if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { | 1303 | if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) { |
| 1272 | if (Settings::values.async_astc.GetValue()) { | 1304 | if (Settings::values.async_astc.GetValue()) { |
| @@ -1471,7 +1503,8 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1471 | auto scaled_info = info; | 1503 | auto scaled_info = info; |
| 1472 | scaled_info.size.width = scaled_width; | 1504 | scaled_info.size.width = scaled_width; |
| 1473 | scaled_info.size.height = scaled_height; | 1505 | scaled_info.size.height = scaled_height; |
| 1474 | scaled_image = MakeImage(runtime->device, runtime->memory_allocator, scaled_info); | 1506 | scaled_image = MakeImage(runtime->device, runtime->memory_allocator, scaled_info, |
| 1507 | runtime->ViewFormats(info.format)); | ||
| 1475 | ignore = false; | 1508 | ignore = false; |
| 1476 | } | 1509 | } |
| 1477 | current_image = *scaled_image; | 1510 | current_image = *scaled_image; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 220943116..6621210ea 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -103,6 +103,10 @@ public: | |||
| 103 | 103 | ||
| 104 | [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size); | 104 | [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size); |
| 105 | 105 | ||
| 106 | std::span<const VkFormat> ViewFormats(PixelFormat format) { | ||
| 107 | return view_formats[static_cast<std::size_t>(format)]; | ||
| 108 | } | ||
| 109 | |||
| 106 | void BarrierFeedbackLoop(); | 110 | void BarrierFeedbackLoop(); |
| 107 | 111 | ||
| 108 | const Device& device; | 112 | const Device& device; |
| @@ -113,6 +117,7 @@ public: | |||
| 113 | RenderPassCache& render_pass_cache; | 117 | RenderPassCache& render_pass_cache; |
| 114 | std::optional<ASTCDecoderPass> astc_decoder_pass; | 118 | std::optional<ASTCDecoderPass> astc_decoder_pass; |
| 115 | const Settings::ResolutionScalingInfo& resolution; | 119 | const Settings::ResolutionScalingInfo& resolution; |
| 120 | std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats; | ||
| 116 | 121 | ||
| 117 | static constexpr size_t indexing_slots = 8 * sizeof(size_t); | 122 | static constexpr size_t indexing_slots = 8 * sizeof(size_t); |
| 118 | std::array<vk::Buffer, indexing_slots> buffers{}; | 123 | std::array<vk::Buffer, indexing_slots> buffers{}; |
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 4db948b6d..01701201d 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp | |||
| @@ -24,7 +24,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) { | |||
| 24 | RemovePendingShaders(); | 24 | RemovePendingShaders(); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { | 27 | void ShaderCache::OnCacheInvalidation(VAddr addr, size_t size) { |
| 28 | std::scoped_lock lock{invalidation_mutex}; | 28 | std::scoped_lock lock{invalidation_mutex}; |
| 29 | InvalidatePagesInRegion(addr, size); | 29 | InvalidatePagesInRegion(addr, size); |
| 30 | } | 30 | } |
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index f3cc4c70b..de8e08002 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h | |||
| @@ -62,7 +62,7 @@ public: | |||
| 62 | /// @brief Unmarks a memory region as cached and marks it for removal | 62 | /// @brief Unmarks a memory region as cached and marks it for removal |
| 63 | /// @param addr Start address of the CPU write operation | 63 | /// @param addr Start address of the CPU write operation |
| 64 | /// @param size Number of bytes of the CPU write operation | 64 | /// @param size Number of bytes of the CPU write operation |
| 65 | void OnCPUWrite(VAddr addr, size_t size); | 65 | void OnCacheInvalidation(VAddr addr, size_t size); |
| 66 | 66 | ||
| 67 | /// @brief Flushes delayed removal operations | 67 | /// @brief Flushes delayed removal operations |
| 68 | void SyncGuestHost(); | 68 | void SyncGuestHost(); |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 8190f3ba1..3a859139c 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -598,6 +598,10 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz | |||
| 598 | [&](ImageId id, Image&) { deleted_images.push_back(id); }); | 598 | [&](ImageId id, Image&) { deleted_images.push_back(id); }); |
| 599 | for (const ImageId id : deleted_images) { | 599 | for (const ImageId id : deleted_images) { |
| 600 | Image& image = slot_images[id]; | 600 | Image& image = slot_images[id]; |
| 601 | if (True(image.flags & ImageFlagBits::CpuModified)) { | ||
| 602 | continue; | ||
| 603 | } | ||
| 604 | image.flags |= ImageFlagBits::CpuModified; | ||
| 601 | if (True(image.flags & ImageFlagBits::Remapped)) { | 605 | if (True(image.flags & ImageFlagBits::Remapped)) { |
| 602 | continue; | 606 | continue; |
| 603 | } | 607 | } |
| @@ -865,11 +869,15 @@ void TextureCache<P>::PopAsyncFlushes() { | |||
| 865 | template <class P> | 869 | template <class P> |
| 866 | ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { | 870 | ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, bool is_upload) { |
| 867 | const ImageInfo dst_info(operand); | 871 | const ImageInfo dst_info(operand); |
| 868 | const ImageId image_id = FindDMAImage(dst_info, operand.address); | 872 | const ImageId dst_id = FindDMAImage(dst_info, operand.address); |
| 869 | if (!image_id) { | 873 | if (!dst_id) { |
| 874 | return NULL_IMAGE_ID; | ||
| 875 | } | ||
| 876 | auto& image = slot_images[dst_id]; | ||
| 877 | if (False(image.flags & ImageFlagBits::GpuModified)) { | ||
| 878 | // No need to waste time on an image that's synced with guest | ||
| 870 | return NULL_IMAGE_ID; | 879 | return NULL_IMAGE_ID; |
| 871 | } | 880 | } |
| 872 | auto& image = slot_images[image_id]; | ||
| 873 | if (image.info.type == ImageType::e3D) { | 881 | if (image.info.type == ImageType::e3D) { |
| 874 | // Don't accelerate 3D images. | 882 | // Don't accelerate 3D images. |
| 875 | return NULL_IMAGE_ID; | 883 | return NULL_IMAGE_ID; |
| @@ -883,7 +891,7 @@ ImageId TextureCache<P>::DmaImageId(const Tegra::DMA::ImageOperand& operand, boo | |||
| 883 | if (!base) { | 891 | if (!base) { |
| 884 | return NULL_IMAGE_ID; | 892 | return NULL_IMAGE_ID; |
| 885 | } | 893 | } |
| 886 | return image_id; | 894 | return dst_id; |
| 887 | } | 895 | } |
| 888 | 896 | ||
| 889 | template <class P> | 897 | template <class P> |
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index a0e10643f..0453456b4 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h | |||
| @@ -54,7 +54,6 @@ enum class RelaxedOptions : u32 { | |||
| 54 | Format = 1 << 1, | 54 | Format = 1 << 1, |
| 55 | Samples = 1 << 2, | 55 | Samples = 1 << 2, |
| 56 | ForceBrokenViews = 1 << 3, | 56 | ForceBrokenViews = 1 << 3, |
| 57 | FormatBpp = 1 << 4, | ||
| 58 | }; | 57 | }; |
| 59 | DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions) | 58 | DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions) |
| 60 | 59 | ||
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 9a618a57a..0de6ed09d 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -1201,8 +1201,7 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const | |||
| 1201 | // Format checking is relaxed, but we still have to check for matching bytes per block. | 1201 | // Format checking is relaxed, but we still have to check for matching bytes per block. |
| 1202 | // This avoids creating a view for blits on UE4 titles where formats with different bytes | 1202 | // This avoids creating a view for blits on UE4 titles where formats with different bytes |
| 1203 | // per block are aliased. | 1203 | // per block are aliased. |
| 1204 | if (BytesPerBlock(existing.format) != BytesPerBlock(candidate.format) && | 1204 | if (BytesPerBlock(existing.format) != BytesPerBlock(candidate.format)) { |
| 1205 | False(options & RelaxedOptions::FormatBpp)) { | ||
| 1206 | return std::nullopt; | 1205 | return std::nullopt; |
| 1207 | } | 1206 | } |
| 1208 | } else { | 1207 | } else { |
| @@ -1233,11 +1232,7 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const | |||
| 1233 | } | 1232 | } |
| 1234 | const bool strict_size = False(options & RelaxedOptions::Size); | 1233 | const bool strict_size = False(options & RelaxedOptions::Size); |
| 1235 | if (!IsBlockLinearSizeCompatible(existing, candidate, base->level, 0, strict_size)) { | 1234 | if (!IsBlockLinearSizeCompatible(existing, candidate, base->level, 0, strict_size)) { |
| 1236 | if (False(options & RelaxedOptions::FormatBpp)) { | 1235 | return std::nullopt; |
| 1237 | return std::nullopt; | ||
| 1238 | } else if (!IsBlockLinearSizeCompatibleBPPRelaxed(existing, candidate, base->level, 0)) { | ||
| 1239 | return std::nullopt; | ||
| 1240 | } | ||
| 1241 | } | 1236 | } |
| 1242 | // TODO: compare block sizes | 1237 | // TODO: compare block sizes |
| 1243 | return base; | 1238 | return base; |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 421e71e5a..e04852e01 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -485,7 +485,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 485 | loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); | 485 | loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); |
| 486 | } | 486 | } |
| 487 | } | 487 | } |
| 488 | if (extensions.extended_dynamic_state2 && (is_radv || is_qualcomm)) { | 488 | if (extensions.extended_dynamic_state2 && is_radv) { |
| 489 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | 489 | const u32 version = (properties.properties.driverVersion << 3) >> 3; |
| 490 | if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { | 490 | if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { |
| 491 | LOG_WARNING( | 491 | LOG_WARNING( |
| @@ -498,6 +498,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 498 | loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); | 498 | loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); |
| 499 | } | 499 | } |
| 500 | } | 500 | } |
| 501 | if (extensions.extended_dynamic_state2 && is_qualcomm) { | ||
| 502 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | ||
| 503 | if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && | ||
| 504 | version < VK_MAKE_API_VERSION(0, 0, 680, 0)) { | ||
| 505 | // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. | ||
| 506 | LOG_WARNING(Render_Vulkan, | ||
| 507 | "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); | ||
| 508 | features.extended_dynamic_state2.extendedDynamicState2 = false; | ||
| 509 | features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; | ||
| 510 | features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; | ||
| 511 | extensions.extended_dynamic_state2 = false; | ||
| 512 | loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); | ||
| 513 | } | ||
| 514 | } | ||
| 501 | if (extensions.extended_dynamic_state3 && is_radv) { | 515 | if (extensions.extended_dynamic_state3 && is_radv) { |
| 502 | LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation"); | 516 | LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation"); |
| 503 | features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; | 517 | features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; |
| @@ -512,8 +526,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 512 | dynamic_state3_enables = false; | 526 | dynamic_state3_enables = false; |
| 513 | } | 527 | } |
| 514 | } | 528 | } |
| 515 | if (extensions.vertex_input_dynamic_state && (is_radv || is_qualcomm)) { | 529 | if (extensions.vertex_input_dynamic_state && is_radv) { |
| 516 | // Qualcomm S8gen2 drivers do not properly support vertex_input_dynamic_state. | ||
| 517 | // TODO(ameerj): Blacklist only offending driver versions | 530 | // TODO(ameerj): Blacklist only offending driver versions |
| 518 | // TODO(ameerj): Confirm if RDNA1 is affected | 531 | // TODO(ameerj): Confirm if RDNA1 is affected |
| 519 | const bool is_rdna2 = | 532 | const bool is_rdna2 = |
| @@ -526,6 +539,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 526 | loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); | 539 | loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); |
| 527 | } | 540 | } |
| 528 | } | 541 | } |
| 542 | if (extensions.vertex_input_dynamic_state && is_qualcomm) { | ||
| 543 | const u32 version = (properties.properties.driverVersion << 3) >> 3; | ||
| 544 | if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && | ||
| 545 | version < VK_MAKE_API_VERSION(0, 0, 680, 0)) { | ||
| 546 | // Qualcomm Adreno 7xx drivers do not properly support vertex_input_dynamic_state. | ||
| 547 | LOG_WARNING( | ||
| 548 | Render_Vulkan, | ||
| 549 | "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); | ||
| 550 | features.vertex_input_dynamic_state.vertexInputDynamicState = false; | ||
| 551 | extensions.vertex_input_dynamic_state = false; | ||
| 552 | loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); | ||
| 553 | } | ||
| 554 | } | ||
| 529 | 555 | ||
| 530 | sets_per_pool = 64; | 556 | sets_per_pool = 64; |
| 531 | if (extensions.extended_dynamic_state3 && is_amd_driver && | 557 | if (extensions.extended_dynamic_state3 && is_amd_driver && |
| @@ -774,6 +800,17 @@ bool Device::ShouldBoostClocks() const { | |||
| 774 | return validated_driver && !is_steam_deck && !is_debugging; | 800 | return validated_driver && !is_steam_deck && !is_debugging; |
| 775 | } | 801 | } |
| 776 | 802 | ||
| 803 | bool Device::HasTimelineSemaphore() const { | ||
| 804 | if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || | ||
| 805 | GetDriverID() == VK_DRIVER_ID_MESA_TURNIP) { | ||
| 806 | // Timeline semaphores do not work properly on all Qualcomm drivers. | ||
| 807 | // They generally work properly with Turnip drivers, but are problematic on some devices | ||
| 808 | // (e.g. ZTE handsets with Snapdragon 870). | ||
| 809 | return false; | ||
| 810 | } | ||
| 811 | return features.timeline_semaphore.timelineSemaphore; | ||
| 812 | } | ||
| 813 | |||
| 777 | bool Device::GetSuitability(bool requires_swapchain) { | 814 | bool Device::GetSuitability(bool requires_swapchain) { |
| 778 | // Assume we will be suitable. | 815 | // Assume we will be suitable. |
| 779 | bool suitable = true; | 816 | bool suitable = true; |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 1f17265d5..be3ed45ff 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -77,6 +77,7 @@ VK_DEFINE_HANDLE(VmaAllocator) | |||
| 77 | EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \ | 77 | EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \ |
| 78 | EXTENSION(KHR, SWAPCHAIN, swapchain) \ | 78 | EXTENSION(KHR, SWAPCHAIN, swapchain) \ |
| 79 | EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ | 79 | EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ |
| 80 | EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \ | ||
| 80 | EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ | 81 | EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ |
| 81 | EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ | 82 | EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ |
| 82 | EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ | 83 | EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ |
| @@ -408,6 +409,11 @@ public: | |||
| 408 | return extensions.workgroup_memory_explicit_layout; | 409 | return extensions.workgroup_memory_explicit_layout; |
| 409 | } | 410 | } |
| 410 | 411 | ||
| 412 | /// Returns true if the device supports VK_KHR_image_format_list. | ||
| 413 | bool IsKhrImageFormatListSupported() const { | ||
| 414 | return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2; | ||
| 415 | } | ||
| 416 | |||
| 411 | /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. | 417 | /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. |
| 412 | bool IsTopologyListPrimitiveRestartSupported() const { | 418 | bool IsTopologyListPrimitiveRestartSupported() const { |
| 413 | return features.primitive_topology_list_restart.primitiveTopologyListRestart; | 419 | return features.primitive_topology_list_restart.primitiveTopologyListRestart; |
| @@ -522,13 +528,7 @@ public: | |||
| 522 | return extensions.shader_atomic_int64; | 528 | return extensions.shader_atomic_int64; |
| 523 | } | 529 | } |
| 524 | 530 | ||
| 525 | bool HasTimelineSemaphore() const { | 531 | bool HasTimelineSemaphore() const; |
| 526 | if (GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY) { | ||
| 527 | // Timeline semaphores do not work properly on all Qualcomm drivers. | ||
| 528 | return false; | ||
| 529 | } | ||
| 530 | return features.timeline_semaphore.timelineSemaphore; | ||
| 531 | } | ||
| 532 | 532 | ||
| 533 | /// Returns the minimum supported version of SPIR-V. | 533 | /// Returns the minimum supported version of SPIR-V. |
| 534 | u32 SupportedSpirvVersion() const { | 534 | u32 SupportedSpirvVersion() const { |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e8418b302..20532416c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -101,6 +101,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 101 | #include "common/settings.h" | 101 | #include "common/settings.h" |
| 102 | #include "common/telemetry.h" | 102 | #include "common/telemetry.h" |
| 103 | #include "core/core.h" | 103 | #include "core/core.h" |
| 104 | #include "core/core_timing.h" | ||
| 104 | #include "core/crypto/key_manager.h" | 105 | #include "core/crypto/key_manager.h" |
| 105 | #include "core/file_sys/card_image.h" | 106 | #include "core/file_sys/card_image.h" |
| 106 | #include "core/file_sys/common_funcs.h" | 107 | #include "core/file_sys/common_funcs.h" |
| @@ -389,6 +390,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 389 | std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>( | 390 | std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>( |
| 390 | Common::Windows::SetCurrentTimerResolutionToMaximum()) | 391 | Common::Windows::SetCurrentTimerResolutionToMaximum()) |
| 391 | .count()); | 392 | .count()); |
| 393 | system->CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution()); | ||
| 392 | #endif | 394 | #endif |
| 393 | UpdateWindowTitle(); | 395 | UpdateWindowTitle(); |
| 394 | 396 | ||
| @@ -452,7 +454,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 452 | // the user through their desktop environment. | 454 | // the user through their desktop environment. |
| 453 | //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the | 455 | //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the |
| 454 | //: computer from sleeping | 456 | //: computer from sleeping |
| 455 | QByteArray wakelock_reason = tr("Running a game").toLatin1(); | 457 | QByteArray wakelock_reason = tr("Running a game").toUtf8(); |
| 456 | SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data()); | 458 | SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data()); |
| 457 | 459 | ||
| 458 | // SDL disables the screen saver by default, and setting the hint | 460 | // SDL disables the screen saver by default, and setting the hint |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 7b6d49c63..d0433ffc6 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include "common/string_util.h" | 21 | #include "common/string_util.h" |
| 22 | #include "common/telemetry.h" | 22 | #include "common/telemetry.h" |
| 23 | #include "core/core.h" | 23 | #include "core/core.h" |
| 24 | #include "core/core_timing.h" | ||
| 24 | #include "core/cpu_manager.h" | 25 | #include "core/cpu_manager.h" |
| 25 | #include "core/crypto/key_manager.h" | 26 | #include "core/crypto/key_manager.h" |
| 26 | #include "core/file_sys/registered_cache.h" | 27 | #include "core/file_sys/registered_cache.h" |
| @@ -316,8 +317,6 @@ int main(int argc, char** argv) { | |||
| 316 | 317 | ||
| 317 | #ifdef _WIN32 | 318 | #ifdef _WIN32 |
| 318 | LocalFree(argv_w); | 319 | LocalFree(argv_w); |
| 319 | |||
| 320 | Common::Windows::SetCurrentTimerResolutionToMaximum(); | ||
| 321 | #endif | 320 | #endif |
| 322 | 321 | ||
| 323 | MicroProfileOnThreadCreate("EmuThread"); | 322 | MicroProfileOnThreadCreate("EmuThread"); |
| @@ -351,6 +350,11 @@ int main(int argc, char** argv) { | |||
| 351 | break; | 350 | break; |
| 352 | } | 351 | } |
| 353 | 352 | ||
| 353 | #ifdef _WIN32 | ||
| 354 | Common::Windows::SetCurrentTimerResolutionToMaximum(); | ||
| 355 | system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution()); | ||
| 356 | #endif | ||
| 357 | |||
| 354 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 358 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 355 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 359 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 356 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | 360 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |