Compare commits

...

57 Commits

Author SHA1 Message Date
0adba08441 Android #178 2024-01-01 00:57:34 +00:00
b7bb6fd44c Merge PR 12543 2024-01-01 00:57:33 +00:00
1f99106eb4 Merge PR 12518 2024-01-01 00:57:33 +00:00
a5377a59a1 Merge PR 12513 2024-01-01 00:57:33 +00:00
8c3dfeb0d6 Merge PR 12501 2024-01-01 00:57:33 +00:00
fe1e6a0448 Merge PR 12466 2024-01-01 00:57:33 +00:00
79cfd1f97e Merge PR 12454 2024-01-01 00:57:33 +00:00
09bfc852dc Merge pull request #12509 from liamwhite/ktrace
k_capabilities: simplify KTrace map skip logic
2023-12-30 14:59:09 -05:00
ace74bd066 Merge pull request #12486 from t895/debug-ci
actions: android: Build relWithDebInfo on main repo
2023-12-30 14:59:04 -05:00
f6ee53af14 Merge pull request #12527 from DCNick3/log-more-sdl-errors
Report more SDL errors
2023-12-30 14:58:58 -05:00
6c6cb5745f Merge pull request #12521 from ReillyBrogan/fix-wayland-appid
Fix Wayland appId
2023-12-30 14:58:52 -05:00
3262c0f747 Merge pull request #12520 from t895/settings-tomfoolery
android: Small settings tweaks
2023-12-30 14:58:45 -05:00
9323a1f9b2 yuzu_cmd: Report more SDL errors 2023-12-30 13:32:33 +03:00
f02a8d0ae9 Merge pull request #12479 from GPUCode/linear-bcn
video_core: Fix buffer_row_length for linear compressed textures
2023-12-30 06:56:08 +01:00
8517d7cb44 Merge pull request #12487 from liamwhite/clip3
shader_recompiler: use default value for clip distances array
2023-12-30 06:54:44 +01:00
cb4b4f3d6e Fix Wayland appId
On compliant Wayland compositors windows are matched to their .desktop files by comparing the appId window property to the name of the .desktop file without the .deskop extension.

Qt5/6 by default set this property to the basename of the binary (IE `yuzu`) which does not match the expected value `org.yuzu_emu.yuzu`. We can fix this and fix window associations on compliant compositors (like Plasma) by using the `setDesktopFileName()` function which will set the appId window property. This is a no-op on X11 so is safe to be ran without guards.
2023-12-29 20:32:08 -06:00
21e7f86697 android: Expose anisotropic filtering setting 2023-12-29 17:43:36 -05:00
347b3bd18d android: Center switch setting title when no description is present 2023-12-29 17:28:01 -05:00
755c45777f android: Pair fastmem toggle to CPU Debug toggle
Hides fastmem toggle when CPU Debugging is disabled
2023-12-29 17:27:36 -05:00
d677052e8c actions: android: Build relWithDebInfo on main repo 2023-12-28 20:53:16 -05:00
95bfc542aa k_capabilities: simplify KTrace map skip logic 2023-12-28 01:22:57 -05:00
d0c60605ab shader_recompiler: use default value for clip distances array 2023-12-26 19:32:47 -05:00
6697b665ca shader_recompiler: respect clip distance limits in indexed store 2023-12-26 19:10:25 -05:00
12178c694a Merge pull request #12455 from liamwhite/end-wait
kernel: use simple mutex for object list container
2023-12-26 11:46:19 -05:00
de1e5584b3 Merge pull request #12465 from liamwhite/proper-handle-table
service: fetch objects from the client handle table
2023-12-26 11:46:11 -05:00
1559984f77 Merge pull request #12471 from FearlessTobi/port-7146
Port citra-emu/citra#7146: "assert/logging: Stop the logging thread and flush the backends before crashing"
2023-12-26 11:46:04 -05:00
467ac4fdfe Merge pull request #12472 from FearlessTobi/port-7239
Port citra-emu/citra#7239: "common: Miscellaneous cleanups"
2023-12-26 11:45:57 -05:00
69b7100dac Merge pull request #12449 from liamwhite/debug-utils
renderer_vulkan: skip SetObjectNameEXT on unsupported driver
2023-12-26 11:45:39 -05:00
14dc41d4b3 Merge pull request #12448 from liamwhite/format-assert
renderer_vulkan: demote format assert to error log
2023-12-26 11:45:33 -05:00
ad049f13aa Merge pull request #12415 from ameerj/ogl-draw-auto
gl_rasterizer: Implement DrawTransformFeedback macro
2023-12-26 11:45:25 -05:00
20e0407235 video_core: Fix buffer_row_length computation for linear compressed textures 2023-12-26 16:33:03 +02:00
4f569fd568 assert/logging: Stop the logging thread and flush the backends before crashing
Co-Authored-By: SachinVin <26602104+SachinVin@users.noreply.github.com>
2023-12-26 10:35:14 +01:00
553dac2ae0 ring_buffer: Use feature macro
Co-Authored-By: GPUCode <47210458+GPUCode@users.noreply.github.com>
2023-12-25 14:10:40 +01:00
96abe0d7d3 main: Remove unused enum
Co-Authored-By: GPUCode <47210458+GPUCode@users.noreply.github.com>
2023-12-25 14:10:05 +01:00
47e44a6693 am/jit: reference memory instance from context 2023-12-24 19:36:42 -05:00
cf8c7d4ed3 kernel: remove unecessary process member from handle table 2023-12-24 19:23:03 -05:00
5165ed9efd service: fetch objects from the client handle table 2023-12-24 19:20:43 -05:00
05e3db3ac9 Merge pull request #12394 from liamwhite/per-process-memory
general: properly support multiple memory instances
2023-12-24 16:23:14 +01:00
e3491a9ee8 kernel: use simple mutex for object list container 2023-12-23 16:26:07 -05:00
6a1ddc5028 renderer_vulkan: skip SetObjectNameEXT on unsupported driver 2023-12-23 11:08:02 -05:00
b1d4804c07 renderer_vulkan: demote format assert to error log 2023-12-23 11:04:02 -05:00
c57ae803a6 kernel: fix resource limit imbalance 2023-12-22 21:52:49 -05:00
db7b2bc8f1 kernel: restrict nce to applications 2023-12-22 21:52:49 -05:00
31bf57a310 general: properly support multiple memory instances 2023-12-22 21:52:49 -05:00
cae675343c k_server_session: remove scratch buffer usage in favor of direct copy 2023-12-22 21:52:49 -05:00
35501ba41c k_server_session: process for guest servers 2023-12-22 21:52:49 -05:00
419055e484 kernel: instantiate memory separately for each guest process 2023-12-22 21:52:49 -05:00
91290b9be4 Merge pull request #12412 from ameerj/gl-query-prims
OpenGL: Add GL_PRIMITIVES_GENERATED and GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries
2023-12-22 11:42:05 -05:00
820f113d9e Merge pull request #12435 from liamwhite/type-check
shader_recompiler: ensure derivatives for textureGrad are f32
2023-12-22 17:41:13 +01:00
373a1ff2ce Merge pull request #12410 from liamwhite/more-mali-null
renderer_vulkan: don't pass null view when nullDescriptor is not supported
2023-12-22 17:40:47 +01:00
4d6b6ba76c Merge pull request #12432 from liamwhite/float-write
shader_recompiler: use float image operations on load/store when required
2023-12-22 17:40:26 +01:00
4aa713e861 shader_recompiler: ensure derivatives for textureGrad are f32 2023-12-21 19:06:33 -05:00
9e9aed41be shader_recompiler: use float image operations on load/store when required 2023-12-21 14:34:46 -05:00
61e8c5f798 gl_rasterizer: Less spammy log for unimplemented resets 2023-12-20 11:51:44 -05:00
bbc0ed118d gl_rasterizer: Implement DrawTransformFeedback macro 2023-12-19 19:54:57 -05:00
db8a601cf8 OpenGL: Add GL_PRIMITIVES_GENERATED and GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries 2023-12-19 17:32:31 -05:00
d0a75580da renderer_vulkan: don't pass null view when nullDescriptor is not supported 2023-12-19 15:13:10 -05:00
165 changed files with 4574 additions and 2130 deletions

View File

@ -6,7 +6,12 @@
export NDK_CCACHE="$(which ccache)" export NDK_CCACHE="$(which ccache)"
ccache -s ccache -s
BUILD_FLAVOR=mainline BUILD_FLAVOR="mainline"
BUILD_TYPE="release"
if [ "${GITHUB_REPOSITORY}" == "yuzu-emu/yuzu" ]; then
BUILD_TYPE="relWithDebInfo"
fi
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
@ -15,7 +20,7 @@ fi
cd src/android cd src/android
chmod +x ./gradlew chmod +x ./gradlew
./gradlew "assemble${BUILD_FLAVOR}Release" "bundle${BUILD_FLAVOR}Release" ./gradlew "assemble${BUILD_FLAVOR}${BUILD_TYPE}" "bundle${BUILD_FLAVOR}${BUILD_TYPE}"
ccache -s ccache -s

View File

@ -7,9 +7,16 @@
REV_NAME="yuzu-${GITDATE}-${GITREV}" REV_NAME="yuzu-${GITDATE}-${GITREV}"
BUILD_FLAVOR=mainline BUILD_FLAVOR="mainline"
cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/release/app-${BUILD_FLAVOR}-release.apk" \ BUILD_TYPE_LOWER="release"
BUILD_TYPE_UPPER="Release"
if [ "${GITHUB_REPOSITORY}" == "yuzu-emu/yuzu" ]; then
BUILD_TYPE_LOWER="relWithDebInfo"
BUILD_TYPE_UPPER="RelWithDebInfo"
fi
cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \
"artifacts/${REV_NAME}.apk" "artifacts/${REV_NAME}.apk"
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}Release"/"app-${BUILD_FLAVOR}-release.aab" \ cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \
"artifacts/${REV_NAME}.aab" "artifacts/${REV_NAME}.aab"

View File

@ -1,3 +1,17 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12454](https://github.com/yuzu-emu/yuzu//pull/12454) | [`3a4e7d45f`](https://github.com/yuzu-emu/yuzu//pull/12454/files) | core_timing: minor refactors | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12466](https://github.com/yuzu-emu/yuzu//pull/12466) | [`adb2af0a2`](https://github.com/yuzu-emu/yuzu//pull/12466/files) | core: track separate heap allocation for linux | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12501](https://github.com/yuzu-emu/yuzu//pull/12501) | [`d1c99c5d5`](https://github.com/yuzu-emu/yuzu//pull/12501/files) | ips_layer: prevent out of bounds access with offset exceeding module size | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12513](https://github.com/yuzu-emu/yuzu//pull/12513) | [`558192abf`](https://github.com/yuzu-emu/yuzu//pull/12513/files) | jit: use code memory handles correctly | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12518](https://github.com/yuzu-emu/yuzu//pull/12518) | [`aa4d15594`](https://github.com/yuzu-emu/yuzu//pull/12518/files) | android: Migrate remaining settings to ini | [t895](https://github.com/t895/) | Yes |
| [12543](https://github.com/yuzu-emu/yuzu//pull/12543) | [`737e6e531`](https://github.com/yuzu-emu/yuzu//pull/12543/files) | VideoCore: A few fixes to DMA and swapchain | [FernandoS27](https://github.com/FernandoS27/) | Yes |
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -10,7 +10,7 @@ plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("kotlin-parcelize") id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21" kotlin("plugin.serialization") version "1.9.20"
id("androidx.navigation.safeargs.kotlin") id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0" id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
} }

View File

@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper import org.yuzu.yuzu_emu.utils.ThemeHelper
import java.text.NumberFormat import java.text.NumberFormat
@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener() stopMotionSensorListener()
} }
override fun onStop() {
super.onStop()
NativeConfig.saveGlobalConfig()
}
override fun onUserLeaveHint() { override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {

View File

@ -18,7 +18,14 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),
PICTURE_IN_PICTURE("picture_in_picture"), PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"); USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"),
HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
SHOW_INPUT_OVERLAY("show_input_overlay"),
TOUCHSCREEN("touchscreen");
override fun getBoolean(needsGlobal: Boolean): Boolean = override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal) NativeConfig.getBoolean(key, needsGlobal)

View File

@ -18,7 +18,12 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_ANTI_ALIASING("anti_aliasing"), RENDERER_ANTI_ALIASING("anti_aliasing"),
RENDERER_SCREEN_LAYOUT("screen_layout"), RENDERER_SCREEN_LAYOUT("screen_layout"),
RENDERER_ASPECT_RATIO("aspect_ratio"), RENDERER_ASPECT_RATIO("aspect_ratio"),
AUDIO_OUTPUT_ENGINE("output_engine"); AUDIO_OUTPUT_ENGINE("output_engine"),
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),
THEME_MODE("theme_mode"),
OVERLAY_SCALE("control_scale"),
OVERLAY_OPACITY("control_opacity");
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@ -15,18 +15,10 @@ object Settings {
SECTION_DEBUG(R.string.preferences_debug); SECTION_DEBUG(R.string.preferences_debug);
} }
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val PREF_OVERLAY_VERSION = "OverlayVersion" // Deprecated input overlay preference keys
const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
val overlayLayoutPrefs = listOf(
PREF_LANDSCAPE_OVERLAY_VERSION,
PREF_PORTRAIT_OVERLAY_VERSION,
PREF_FOLDABLE_OVERLAY_VERSION
)
const val PREF_CONTROL_SCALE = "controlScale" const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity" const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled" const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@ -47,23 +39,12 @@ object Settings {
const val PREF_BUTTON_STICK_R = "buttonToggle14" const val PREF_BUTTON_STICK_R = "buttonToggle14"
const val PREF_BUTTON_HOME = "buttonToggle15" const val PREF_BUTTON_HOME = "buttonToggle15"
const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
const val PREF_THEME = "Theme"
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
val overlayPreferences = listOf( val overlayPreferences = listOf(
PREF_OVERLAY_VERSION,
PREF_CONTROL_SCALE,
PREF_CONTROL_OPACITY,
PREF_TOUCH_ENABLED,
PREF_BUTTON_A, PREF_BUTTON_A,
PREF_BUTTON_B, PREF_BUTTON_B,
PREF_BUTTON_X, PREF_BUTTON_X,
@ -83,6 +64,21 @@ object Settings {
PREF_BUTTON_STICK_R PREF_BUTTON_STICK_R
) )
// Deprecated layout preference keys
const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
const val PREF_PORTRAIT_SUFFIX = "_Portrait"
const val PREF_FOLDABLE_SUFFIX = "_Foldable"
val overlayLayoutSuffixes = listOf(
PREF_LANDSCAPE_SUFFIX,
PREF_PORTRAIT_SUFFIX,
PREF_FOLDABLE_SUFFIX
)
// Deprecated theme preference keys
const val PREF_THEME = "Theme"
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val LayoutOption_Unspecified = 0 const val LayoutOption_Unspecified = 0
const val LayoutOption_MobilePortrait = 4 const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5 const val LayoutOption_MobileLandscape = 5

View File

@ -243,6 +243,15 @@ abstract class SettingsItem(
R.string.renderer_reactive_flushing_description R.string.renderer_reactive_flushing_description
) )
) )
put(
SingleChoiceSetting(
IntSetting.MAX_ANISOTROPY,
R.string.anisotropic_filtering,
R.string.anisotropic_filtering_description,
R.array.anisoEntries,
R.array.anisoValues
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.AUDIO_OUTPUT_ENGINE, IntSetting.AUDIO_OUTPUT_ENGINE,
@ -298,6 +307,7 @@ abstract class SettingsItem(
override val key: String = FASTMEM_COMBINED override val key: String = FASTMEM_COMBINED
override val isRuntimeModifiable: Boolean = false override val isRuntimeModifiable: Boolean = false
override val pairedSettingKey = BooleanSetting.CPU_DEBUG_MODE.key
override val defaultValue: Boolean = true override val defaultValue: Boolean = true
override val isSwitchable: Boolean = true override val isSwitchable: Boolean = true
override var global: Boolean override var global: Boolean

View File

@ -3,10 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
@ -29,9 +27,6 @@ class SettingsFragmentPresenter(
) { ) {
private var settingsList = ArrayList<SettingsItem>() private var settingsList = ArrayList<SettingsItem>()
private val preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
// Extension for altering settings list based on each setting's properties // Extension for altering settings list based on each setting's properties
fun ArrayList<SettingsItem>.add(key: String) { fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!! val item = SettingsItem.settingsItems[key]!!
@ -149,6 +144,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_VSYNC.key) add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key) add(IntSetting.RENDERER_SCALING_FILTER.key)
add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key) add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key) add(BooleanSetting.PICTURE_IN_PICTURE.key)
@ -169,25 +165,19 @@ class SettingsFragmentPresenter(
private fun addThemeSettings(sl: ArrayList<SettingsItem>) { private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
val theme: AbstractIntSetting = object : AbstractIntSetting { val theme: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int = override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
preferences.getInt(Settings.PREF_THEME, 0)
override fun setInt(value: Int) { override fun setInt(value: Int) {
preferences.edit() IntSetting.THEME.setInt(value)
.putInt(Settings.PREF_THEME, value)
.apply()
settingsViewModel.setShouldRecreate(true) settingsViewModel.setShouldRecreate(true)
} }
override val key: String = Settings.PREF_THEME override val key: String = IntSetting.THEME.key
override val isRuntimeModifiable: Boolean = false override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() override fun getValueAsString(needsGlobal: Boolean): String =
override val defaultValue: Int = 0 IntSetting.THEME.getValueAsString()
override fun reset() {
preferences.edit() override val defaultValue: Int = IntSetting.THEME.defaultValue
.putInt(Settings.PREF_THEME, defaultValue) override fun reset() = IntSetting.THEME.setInt(defaultValue)
.apply()
}
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@ -213,24 +203,22 @@ class SettingsFragmentPresenter(
} }
val themeMode: AbstractIntSetting = object : AbstractIntSetting { val themeMode: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int = override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
preferences.getInt(Settings.PREF_THEME_MODE, -1)
override fun setInt(value: Int) { override fun setInt(value: Int) {
preferences.edit() IntSetting.THEME_MODE.setInt(value)
.putInt(Settings.PREF_THEME_MODE, value)
.apply()
settingsViewModel.setShouldRecreate(true) settingsViewModel.setShouldRecreate(true)
} }
override val key: String = Settings.PREF_THEME_MODE override val key: String = IntSetting.THEME_MODE.key
override val isRuntimeModifiable: Boolean = false override val isRuntimeModifiable: Boolean =
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() IntSetting.THEME_MODE.isRuntimeModifiable
override val defaultValue: Int = -1
override fun getValueAsString(needsGlobal: Boolean): String =
IntSetting.THEME_MODE.getValueAsString()
override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
override fun reset() { override fun reset() {
preferences.edit() IntSetting.THEME_MODE.setInt(defaultValue)
.putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
.apply()
settingsViewModel.setShouldRecreate(true) settingsViewModel.setShouldRecreate(true)
} }
} }
@ -247,25 +235,24 @@ class SettingsFragmentPresenter(
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean = override fun getBoolean(needsGlobal: Boolean): Boolean =
preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
override fun setBoolean(value: Boolean) { override fun setBoolean(value: Boolean) {
preferences.edit() BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
.apply()
settingsViewModel.setShouldRecreate(true) settingsViewModel.setShouldRecreate(true)
} }
override val key: String = Settings.PREF_BLACK_BACKGROUNDS override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
override val isRuntimeModifiable: Boolean = false override val isRuntimeModifiable: Boolean =
override fun getValueAsString(needsGlobal: Boolean): String = BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
getBoolean().toString()
override val defaultValue: Boolean = false override fun getValueAsString(needsGlobal: Boolean): String =
BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
override fun reset() { override fun reset() {
preferences.edit() BooleanSetting.BLACK_BACKGROUNDS
.putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue) .setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
.apply()
settingsViewModel.setShouldRecreate(true) settingsViewModel.setShouldRecreate(true)
} }
} }

View File

@ -7,7 +7,6 @@ import android.annotation.SuppressLint
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.SharedPreferences
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri import android.net.Uri
@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import androidx.window.layout.FoldingFeature import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo import androidx.window.layout.WindowLayoutInfo
@ -46,22 +44,22 @@ import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.lang.NullPointerException import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback { class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var preferences: SharedPreferences
private lateinit var emulationState: EmulationState private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null private var perfStatsUpdater: (() -> Unit)? = null
@ -141,7 +139,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation. // So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true retainInstance = true
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
emulationState = EmulationState(game.path) emulationState = EmulationState(game.path)
} }
@ -382,24 +379,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
updateScreenLayout() updateScreenLayout()
val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
if (emulationActivity?.isInPictureInPictureMode == true) { if (emulationActivity?.isInPictureInPictureMode == true) {
if (binding.drawerLayout.isOpen) { if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close() binding.drawerLayout.close()
} }
if (EmulationMenuSettings.showOverlay) { if (showInputOverlay) {
binding.surfaceInputOverlay.visibility = View.INVISIBLE binding.surfaceInputOverlay.visibility = View.INVISIBLE
} }
} else { } else {
if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) { if (showInputOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.visibility = View.VISIBLE binding.surfaceInputOverlay.visibility = View.VISIBLE
} else { } else {
binding.surfaceInputOverlay.visibility = View.INVISIBLE binding.surfaceInputOverlay.visibility = View.INVISIBLE
} }
if (!isInFoldableLayout) { if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
} else { } else {
binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
} }
} }
} }
@ -423,17 +421,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
private fun resetInputOverlay() { private fun resetInputOverlay() {
preferences.edit() IntSetting.OVERLAY_SCALE.reset()
.remove(Settings.PREF_CONTROL_SCALE) IntSetting.OVERLAY_OPACITY.reset()
.remove(Settings.PREF_CONTROL_OPACITY)
.apply()
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
} }
} }
private fun updateShowFpsOverlay() { private fun updateShowFpsOverlay() {
if (EmulationMenuSettings.showFps) { if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
val SYSTEM_FPS = 0 val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val FRAMETIME = 2 val FRAMETIME = 2
@ -496,7 +492,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.inGameMenu.layoutParams.height = it.bounds.bottom binding.inGameMenu.layoutParams.height = it.bounds.bottom
isInFoldableLayout = true isInFoldableLayout = true
binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
} }
} }
it.isSeparating it.isSeparating
@ -535,18 +531,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
popup.menu.apply { popup.menu.apply {
findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps findItem(R.id.menu_toggle_fps).isChecked =
findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide findItem(R.id.menu_rel_stick_center).isChecked =
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
findItem(R.id.menu_show_overlay).isChecked =
BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
} }
popup.setOnMenuItemClickListener { popup.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.menu_toggle_fps -> { R.id.menu_toggle_fps -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
EmulationMenuSettings.showFps = it.isChecked BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
updateShowFpsOverlay() updateShowFpsOverlay()
true true
} }
@ -564,11 +564,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
R.id.menu_toggle_controls -> { R.id.menu_toggle_controls -> {
val preferences = val overlayControlData = NativeConfig.getOverlayControlData()
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) val optionsArray = BooleanArray(overlayControlData.size)
val optionsArray = BooleanArray(Settings.overlayPreferences.size) overlayControlData.forEachIndexed { i, _ ->
Settings.overlayPreferences.forEachIndexed { i, _ -> optionsArray[i] = overlayControlData.firstOrNull { data ->
optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15) OverlayControl.entries[i].id == data.id
}?.enabled == true
} }
val dialog = MaterialAlertDialogBuilder(requireContext()) val dialog = MaterialAlertDialogBuilder(requireContext())
@ -577,11 +578,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.array.gamepadButtons, R.array.gamepadButtons,
optionsArray optionsArray
) { _, indexSelected, isChecked -> ) { _, indexSelected, isChecked ->
preferences.edit() overlayControlData.firstOrNull { data ->
.putBoolean("buttonToggle$indexSelected", isChecked) OverlayControl.entries[indexSelected].id == data.id
.apply() }?.enabled = isChecked
} }
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
NativeConfig.setOverlayControlData(overlayControlData)
NativeConfig.saveGlobalConfig()
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
} }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
@ -592,12 +595,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL) dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
.setOnClickListener { .setOnClickListener {
val isChecked = !optionsArray[0] val isChecked = !optionsArray[0]
Settings.overlayPreferences.forEachIndexed { i, _ -> overlayControlData.forEachIndexed { i, _ ->
optionsArray[i] = isChecked optionsArray[i] = isChecked
dialog.listView.setItemChecked(i, isChecked) dialog.listView.setItemChecked(i, isChecked)
preferences.edit() overlayControlData[i].enabled = isChecked
.putBoolean("buttonToggle$i", isChecked)
.apply()
} }
} }
true true
@ -605,26 +606,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_show_overlay -> { R.id.menu_show_overlay -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
EmulationMenuSettings.showOverlay = it.isChecked BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
true true
} }
R.id.menu_rel_stick_center -> { R.id.menu_rel_stick_center -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
EmulationMenuSettings.joystickRelCenter = it.isChecked BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
true true
} }
R.id.menu_dpad_slide -> { R.id.menu_dpad_slide -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
EmulationMenuSettings.dpadSlide = it.isChecked BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
true true
} }
R.id.menu_haptics -> { R.id.menu_haptics -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
EmulationMenuSettings.hapticFeedback = it.isChecked BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
true
}
R.id.menu_touchscreen -> {
it.isChecked = !it.isChecked
BooleanSetting.TOUCHSCREEN.setBoolean(it.isChecked)
true true
} }
@ -667,6 +674,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} }
} }
NativeConfig.saveGlobalConfig()
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -675,7 +683,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
adjustBinding.apply { adjustBinding.apply {
inputScaleSlider.apply { inputScaleSlider.apply {
valueTo = 150F valueTo = 150F
value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
addOnChangeListener( addOnChangeListener(
Slider.OnChangeListener { _, value, _ -> Slider.OnChangeListener { _, value, _ ->
inputScaleValue.text = "${value.toInt()}%" inputScaleValue.text = "${value.toInt()}%"
@ -685,7 +693,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
inputOpacitySlider.apply { inputOpacitySlider.apply {
valueTo = 100F valueTo = 100F
value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat() value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
addOnChangeListener( addOnChangeListener(
Slider.OnChangeListener { _, value, _ -> Slider.OnChangeListener { _, value, _ ->
inputOpacityValue.text = "${value.toInt()}%" inputOpacityValue.text = "${value.toInt()}%"
@ -709,16 +717,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
private fun setControlScale(scale: Int) { private fun setControlScale(scale: Int) {
preferences.edit() IntSetting.OVERLAY_SCALE.setInt(scale)
.putInt(Settings.PREF_CONTROL_SCALE, scale)
.apply()
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
} }
private fun setControlOpacity(opacity: Int) { private fun setControlOpacity(opacity: Int) {
preferences.edit() IntSetting.OVERLAY_OPACITY.setInt(opacity)
.putInt(Settings.PREF_CONTROL_OPACITY, opacity)
.apply()
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
} }

View File

@ -10,6 +10,7 @@ import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary.ButtonState import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
/** /**
* Custom [BitmapDrawable] that is capable * Custom [BitmapDrawable] that is capable
@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
defaultStateBitmap: Bitmap, defaultStateBitmap: Bitmap,
pressedStateBitmap: Bitmap, pressedStateBitmap: Bitmap,
val buttonId: Int, val buttonId: Int,
val prefId: String val overlayControlData: OverlayControlData
) { ) {
// The ID value what motion event is tracking // The ID value what motion event is tracking
var trackId: Int var trackId: Int

View File

@ -14,7 +14,7 @@ import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
import kotlin.math.sqrt import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
/** /**
* Custom [BitmapDrawable] that is capable * Custom [BitmapDrawable] that is capable
@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
pressedState = true pressedState = true
outerBitmap.alpha = 0 outerBitmap.alpha = 0
boundsBoxBitmap.alpha = opacity boundsBoxBitmap.alpha = opacity
if (EmulationMenuSettings.joystickRelCenter) { if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
virtBounds.offset( virtBounds.offset(
xPosition - virtBounds.centerX(), xPosition - virtBounds.centerX(),
yPosition - virtBounds.centerY() yPosition - virtBounds.centerY()

View File

@ -0,0 +1,188 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
import androidx.annotation.IntegerRes
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
enum class OverlayControl(
val id: String,
val defaultVisibility: Boolean,
@IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
@IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
@IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
) {
BUTTON_A(
"button_a",
true,
Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
),
BUTTON_B(
"button_b",
true,
Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
),
BUTTON_X(
"button_x",
true,
Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
),
BUTTON_Y(
"button_y",
true,
Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
),
BUTTON_PLUS(
"button_plus",
true,
Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
),
BUTTON_MINUS(
"button_minus",
true,
Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
),
BUTTON_HOME(
"button_home",
false,
Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
),
BUTTON_CAPTURE(
"button_capture",
false,
Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
),
BUTTON_L(
"button_l",
true,
Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
),
BUTTON_R(
"button_r",
true,
Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
),
BUTTON_ZL(
"button_zl",
true,
Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
),
BUTTON_ZR(
"button_zr",
true,
Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
),
BUTTON_STICK_L(
"button_stick_l",
true,
Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
),
BUTTON_STICK_R(
"button_stick_r",
true,
Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
),
STICK_L(
"stick_l",
true,
Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
),
STICK_R(
"stick_r",
true,
Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
),
COMBINED_DPAD(
"combined_dpad",
true,
Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
);
fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
val rawResourcePair: Pair<Int, Int>
YuzuApplication.appContext.resources.apply {
rawResourcePair = when (layout) {
OverlayLayout.Landscape -> {
Pair(
getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
)
}
OverlayLayout.Portrait -> {
Pair(
getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
)
}
OverlayLayout.Foldable -> {
Pair(
getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
)
}
}
}
return Pair(
rawResourcePair.first.toDouble() / 1000,
rawResourcePair.second.toDouble() / 1000
)
}
fun toOverlayControlData(): OverlayControlData =
OverlayControlData(
id,
defaultVisibility,
getDefaultPositionForLayout(OverlayLayout.Landscape),
getDefaultPositionForLayout(OverlayLayout.Portrait),
getDefaultPositionForLayout(OverlayLayout.Foldable)
)
companion object {
val map: HashMap<String, OverlayControl> by lazy {
val hashMap = hashMapOf<String, OverlayControl>()
entries.forEach { hashMap[it.id] = it }
hashMap
}
fun from(id: String): OverlayControl? = map[id]
}
}

View File

@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
data class OverlayControlData(
val id: String,
var enabled: Boolean,
var landscapePosition: Pair<Double, Double>,
var portraitPosition: Pair<Double, Double>,
var foldablePosition: Pair<Double, Double>
) {
fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
when (layout) {
OverlayLayout.Landscape -> landscapePosition
OverlayLayout.Portrait -> portraitPosition
OverlayLayout.Foldable -> foldablePosition
}
}

View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
import androidx.annotation.IntegerRes
data class OverlayControlDefault(
val buttonId: String,
@IntegerRes val landscapePositionResource: Pair<Int, Int>,
@IntegerRes val portraitPositionResource: Pair<Int, Int>,
@IntegerRes val foldablePositionResource: Pair<Int, Int>
)

View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.overlay.model
enum class OverlayLayout(val id: String) {
Landscape("Landscape"),
Portrait("Portrait"),
Foldable("Foldable")
}

View File

@ -3,9 +3,17 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import androidx.preference.PreferenceManager
import java.io.IOException import java.io.IOException
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
object DirectoryInitialization { object DirectoryInitialization {
private var userPath: String? = null private var userPath: String? = null
@ -17,6 +25,7 @@ object DirectoryInitialization {
initializeInternalStorage() initializeInternalStorage()
NativeLibrary.initializeSystem(false) NativeLibrary.initializeSystem(false)
NativeConfig.initializeGlobalConfig() NativeConfig.initializeGlobalConfig()
migrateSettings()
areDirectoriesReady = true areDirectoriesReady = true
} }
} }
@ -35,4 +44,170 @@ object DirectoryInitialization {
e.printStackTrace() e.printStackTrace()
} }
} }
private fun migrateSettings() {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
var saveConfig = false
val theme = preferences.migratePreference<Int>(Settings.PREF_THEME)
if (theme != null) {
IntSetting.THEME.setInt(theme)
saveConfig = true
}
val themeMode = preferences.migratePreference<Int>(Settings.PREF_THEME_MODE)
if (themeMode != null) {
IntSetting.THEME_MODE.setInt(themeMode)
saveConfig = true
}
val blackBackgrounds =
preferences.migratePreference<Boolean>(Settings.PREF_BLACK_BACKGROUNDS)
if (blackBackgrounds != null) {
BooleanSetting.BLACK_BACKGROUNDS.setBoolean(blackBackgrounds)
saveConfig = true
}
val joystickRelCenter =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
if (joystickRelCenter != null) {
BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
saveConfig = true
}
val dpadSlide =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
if (dpadSlide != null) {
BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
saveConfig = true
}
val hapticFeedback =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
if (hapticFeedback != null) {
BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
saveConfig = true
}
val showPerformanceOverlay =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
if (showPerformanceOverlay != null) {
BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
saveConfig = true
}
val showInputOverlay =
preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
if (showInputOverlay != null) {
BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
saveConfig = true
}
val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
if (overlayOpacity != null) {
IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
saveConfig = true
}
val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
if (overlayScale != null) {
IntSetting.OVERLAY_SCALE.setInt(overlayScale)
saveConfig = true
}
var setOverlayData = false
val overlayControlData = NativeConfig.getOverlayControlData()
if (overlayControlData.isEmpty()) {
val overlayControlDataMap =
NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
for (button in Settings.overlayPreferences) {
val buttonId = convertButtonId(button)
var buttonEnabled = preferences.migratePreference<Boolean>(button)
if (buttonEnabled == null) {
buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
}
var landscapeXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
)?.toDouble()
var landscapeYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
)?.toDouble()
if (landscapeXPosition == null || landscapeYPosition == null) {
val landscapePosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
landscapeXPosition = landscapePosition.first
landscapeYPosition = landscapePosition.second
}
var portraitXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
)?.toDouble()
var portraitYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
)?.toDouble()
if (portraitXPosition == null || portraitYPosition == null) {
val portraitPosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
portraitXPosition = portraitPosition.first
portraitYPosition = portraitPosition.second
}
var foldableXPosition = preferences.migratePreference<Float>(
"$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
)?.toDouble()
var foldableYPosition = preferences.migratePreference<Float>(
"$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
)?.toDouble()
if (foldableXPosition == null || foldableYPosition == null) {
val foldablePosition = OverlayControl.map[buttonId]
?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
foldableXPosition = foldablePosition.first
foldableYPosition = foldablePosition.second
}
val controlData = OverlayControlData(
buttonId,
buttonEnabled,
Pair(landscapeXPosition, landscapeYPosition),
Pair(portraitXPosition, portraitYPosition),
Pair(foldableXPosition, foldableYPosition)
)
overlayControlDataMap[buttonId] = controlData
setOverlayData = true
}
if (setOverlayData) {
NativeConfig.setOverlayControlData(
overlayControlDataMap.map { it.value }.toTypedArray()
)
saveConfig = true
}
}
if (saveConfig) {
NativeConfig.saveGlobalConfig()
}
}
private fun convertButtonId(buttonId: String): String =
when (buttonId) {
Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
else -> ""
}
} }

View File

@ -1,50 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
object EmulationMenuSettings {
private val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
var joystickRelCenter: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
.apply()
}
var dpadSlide: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
.apply()
}
var hapticFeedback: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
.apply()
}
var showFps: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
.apply()
}
var showOverlay: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
set(value) {
preferences.edit()
.putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
.apply()
}
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
object NativeConfig { object NativeConfig {
/** /**
@ -150,4 +151,21 @@ object NativeConfig {
*/ */
@Synchronized @Synchronized
external fun setDisabledAddons(programId: String, disabledAddons: Array<String>) external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
/**
* Gets an array of [OverlayControlData] from settings
*
* @return An array of [OverlayControlData]
*/
@Synchronized
external fun getOverlayControlData(): Array<OverlayControlData>
/**
* Clears the AndroidSettings::values.overlay_control_data array and replaces its values
* with [overlayControlData]
*
* @param overlayControlData Replacement array of [OverlayControlData]
*/
@Synchronized
external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
} }

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
object PreferenceUtil {
/**
* Retrieves a shared preference value and then deletes the value in storage.
* @param key Associated key for the value in this preferences instance
* @return Typed value associated with [key]. Null if no such key exists.
*/
inline fun <reified T> SharedPreferences.migratePreference(key: String): T? {
if (!this.contains(key)) {
return null
}
val value: Any = when (T::class) {
String::class -> this.getString(key, "")!!
Boolean::class -> this.getBoolean(key, false)
Int::class -> this.getInt(key, 0)
Float::class -> this.getFloat(key, 0f)
Long::class -> this.getLong(key, 0)
else -> throw IllegalStateException("Tried to migrate preference with invalid type!")
}
deletePreference(key)
return value as T
}
fun SharedPreferences.deletePreference(key: String) = this.edit().remove(key).apply()
}

View File

@ -5,38 +5,38 @@ package org.yuzu.yuzu_emu.utils
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.os.Build
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
import kotlin.math.roundToInt import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.ui.main.ThemeProvider import org.yuzu.yuzu_emu.ui.main.ThemeProvider
object ThemeHelper { object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f const val SYSTEM_BAR_ALPHA = 0.9f
private const val DEFAULT = 0
private const val MATERIAL_YOU = 1
fun setTheme(activity: AppCompatActivity) { fun setTheme(activity: AppCompatActivity) {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
setThemeMode(activity) setThemeMode(activity)
when (preferences.getInt(Settings.PREF_THEME, 0)) { when (Theme.from(IntSetting.THEME.getInt())) {
DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main) Theme.Default -> activity.setTheme(R.style.Theme_Yuzu_Main)
MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou) Theme.MaterialYou -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
} else {
activity.setTheme(R.style.Theme_Yuzu_Main)
}
}
} }
// Using a specific night mode check because this could apply incorrectly when using the // Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity // light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds. // will then show light mode colors/navigation bars but with black backgrounds.
if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) && if (BooleanSetting.BLACK_BACKGROUNDS.getBoolean() && isNightMode(activity)) {
isNightMode(activity)
) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark) activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
} }
} }
@ -60,8 +60,7 @@ object ThemeHelper {
} }
fun setThemeMode(activity: AppCompatActivity) { fun setThemeMode(activity: AppCompatActivity) {
val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) val themeMode = IntSetting.THEME_MODE.getInt()
.getInt(Settings.PREF_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
activity.delegate.localNightMode = themeMode activity.delegate.localNightMode = themeMode
val windowController = WindowCompat.getInsetsController( val windowController = WindowCompat.getInsetsController(
activity.window, activity.window,
@ -95,3 +94,12 @@ object ThemeHelper {
windowController.isAppearanceLightNavigationBars = false windowController.isAppearanceLightNavigationBars = false
} }
} }
enum class Theme(val int: Int) {
Default(0),
MaterialYou(1);
companion object {
fun from(int: Int): Theme = entries.firstOrNull { it.int == int } ?: Default
}
}

View File

@ -9,6 +9,7 @@
#include <jni.h> #include <jni.h>
#include "common/string_util.h" #include "common/string_util.h"
#include "jni/id_cache.h"
std::string GetJString(JNIEnv* env, jstring jstr) { std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) { if (!jstr) {
@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
jstring ToJString(JNIEnv* env, std::u16string_view str) { jstring ToJString(JNIEnv* env, std::u16string_view str) {
return ToJString(env, Common::UTF16ToUTF8(str)); return ToJString(env, Common::UTF16ToUTF8(str));
} }
double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
}
jobject ToJDouble(JNIEnv* env, double value) {
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
}

View File

@ -10,3 +10,6 @@
std::string GetJString(JNIEnv* env, jstring jstr); std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str); jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_view str); jstring ToJString(JNIEnv* env, std::u16string_view str);
double GetJDouble(JNIEnv* env, jobject jdouble);
jobject ToJDouble(JNIEnv* env, double value);

View File

@ -35,6 +35,7 @@ void AndroidConfig::ReadAndroidValues() {
if (global) { if (global) {
ReadAndroidUIValues(); ReadAndroidUIValues();
ReadUIValues(); ReadUIValues();
ReadOverlayValues();
} }
ReadDriverValues(); ReadDriverValues();
} }
@ -81,10 +82,42 @@ void AndroidConfig::ReadDriverValues() {
EndGroup(); EndGroup();
} }
void AndroidConfig::ReadOverlayValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
ReadCategory(Settings::Category::Overlay);
AndroidSettings::values.overlay_control_data.clear();
const int control_data_size = BeginArray("control_data");
for (int i = 0; i < control_data_size; ++i) {
SetArrayIndex(i);
AndroidSettings::OverlayControlData control_data;
control_data.id = ReadStringSetting(std::string("id"));
control_data.enabled = ReadBooleanSetting(std::string("enabled"));
control_data.landscape_position.first =
ReadDoubleSetting(std::string("landscape\\x_position"));
control_data.landscape_position.second =
ReadDoubleSetting(std::string("landscape\\y_position"));
control_data.portrait_position.first =
ReadDoubleSetting(std::string("portrait\\x_position"));
control_data.portrait_position.second =
ReadDoubleSetting(std::string("portrait\\y_position"));
control_data.foldable_position.first =
ReadDoubleSetting(std::string("foldable\\x_position"));
control_data.foldable_position.second =
ReadDoubleSetting(std::string("foldable\\y_position"));
AndroidSettings::values.overlay_control_data.push_back(control_data);
}
EndArray();
EndGroup();
}
void AndroidConfig::SaveAndroidValues() { void AndroidConfig::SaveAndroidValues() {
if (global) { if (global) {
SaveAndroidUIValues(); SaveAndroidUIValues();
SaveUIValues(); SaveUIValues();
SaveOverlayValues();
} }
SaveDriverValues(); SaveDriverValues();
@ -114,8 +147,9 @@ void AndroidConfig::SavePathValues() {
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
SetArrayIndex(i); SetArrayIndex(i);
const auto& game_dir = AndroidSettings::values.game_dirs[i]; const auto& game_dir = AndroidSettings::values.game_dirs[i];
WriteSetting(std::string("path"), game_dir.path); WriteStringSetting(std::string("path"), game_dir.path);
WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
std::make_optional(false));
} }
EndArray(); EndArray();
@ -130,6 +164,35 @@ void AndroidConfig::SaveDriverValues() {
EndGroup(); EndGroup();
} }
void AndroidConfig::SaveOverlayValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
WriteCategory(Settings::Category::Overlay);
BeginArray("control_data");
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
SetArrayIndex(i);
const auto& control_data = AndroidSettings::values.overlay_control_data[i];
WriteStringSetting(std::string("id"), control_data.id);
WriteBooleanSetting(std::string("enabled"), control_data.enabled);
WriteDoubleSetting(std::string("landscape\\x_position"),
control_data.landscape_position.first);
WriteDoubleSetting(std::string("landscape\\y_position"),
control_data.landscape_position.second);
WriteDoubleSetting(std::string("portrait\\x_position"),
control_data.portrait_position.first);
WriteDoubleSetting(std::string("portrait\\y_position"),
control_data.portrait_position.second);
WriteDoubleSetting(std::string("foldable\\x_position"),
control_data.foldable_position.first);
WriteDoubleSetting(std::string("foldable\\y_position"),
control_data.foldable_position.second);
}
EndArray();
EndGroup();
}
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category; auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) { if (map.contains(category)) {

View File

@ -18,6 +18,7 @@ protected:
void ReadAndroidValues(); void ReadAndroidValues();
void ReadAndroidUIValues(); void ReadAndroidUIValues();
void ReadDriverValues(); void ReadDriverValues();
void ReadOverlayValues();
void ReadHidbusValues() override {} void ReadHidbusValues() override {}
void ReadDebugControlValues() override {} void ReadDebugControlValues() override {}
void ReadPathValues() override; void ReadPathValues() override;
@ -30,6 +31,7 @@ protected:
void SaveAndroidValues(); void SaveAndroidValues();
void SaveAndroidUIValues(); void SaveAndroidUIValues();
void SaveDriverValues(); void SaveDriverValues();
void SaveOverlayValues();
void SaveHidbusValues() override {} void SaveHidbusValues() override {}
void SaveDebugControlValues() override {} void SaveDebugControlValues() override {}
void SavePathValues() override; void SavePathValues() override;

View File

@ -14,6 +14,14 @@ struct GameDir {
bool deep_scan = false; bool deep_scan = false;
}; };
struct OverlayControlData {
std::string id;
bool enabled;
std::pair<double, double> landscape_position;
std::pair<double, double> portrait_position;
std::pair<double, double> foldable_position;
};
struct Values { struct Values {
Settings::Linkage linkage; Settings::Linkage linkage;
@ -33,6 +41,28 @@ struct Values {
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
Settings::Category::GpuDriver}; Settings::Category::GpuDriver};
Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
Settings::Category::Android};
// Input/performance overlay settings
std::vector<OverlayControlData> overlay_control_data;
Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
Settings::Category::Overlay};
Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
Settings::Category::Overlay};
Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
Settings::Category::Overlay};
Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
}; };
extern Values values; extern Values values;

View File

@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field; static jfieldID s_pair_first_field;
static jfieldID s_pair_second_field; static jfieldID s_pair_second_field;
static jclass s_overlay_control_data_class;
static jmethodID s_overlay_control_data_constructor;
static jfieldID s_overlay_control_data_id_field;
static jfieldID s_overlay_control_data_enabled_field;
static jfieldID s_overlay_control_data_landscape_position_field;
static jfieldID s_overlay_control_data_portrait_position_field;
static jfieldID s_overlay_control_data_foldable_position_field;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jfieldID s_double_value_field;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache { namespace IDCache {
@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
return s_pair_second_field; return s_pair_second_field;
} }
jclass GetOverlayControlDataClass() {
return s_overlay_control_data_class;
}
jmethodID GetOverlayControlDataConstructor() {
return s_overlay_control_data_constructor;
}
jfieldID GetOverlayControlDataIdField() {
return s_overlay_control_data_id_field;
}
jfieldID GetOverlayControlDataEnabledField() {
return s_overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return s_overlay_control_data_landscape_position_field;
}
jfieldID GetOverlayControlDataPortraitPositionField() {
return s_overlay_control_data_portrait_position_field;
}
jfieldID GetOverlayControlDataFoldablePositionField() {
return s_overlay_control_data_foldable_position_field;
}
jclass GetDoubleClass() {
return s_double_class;
}
jmethodID GetDoubleConstructor() {
return s_double_constructor;
}
jfieldID GetDoubleValueField() {
return s_double_value_field;
}
} // namespace IDCache } // namespace IDCache
#ifdef __cplusplus #ifdef __cplusplus
@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class); env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
env->DeleteLocalRef(overlay_control_data_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_field = env->GetFieldID(double_class, "value", "D");
env->DeleteLocalRef(double_class);
// Initialize Android Storage // Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class); Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
env->DeleteGlobalRef(s_game_class); env->DeleteGlobalRef(s_game_class);
env->DeleteGlobalRef(s_string_class); env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(s_pair_class); env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(s_double_class);
// UnInitialize applets // UnInitialize applets
SoftwareKeyboard::CleanupJNI(env); SoftwareKeyboard::CleanupJNI(env);

View File

@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
jfieldID GetPairFirstField(); jfieldID GetPairFirstField();
jfieldID GetPairSecondField(); jfieldID GetPairSecondField();
jclass GetOverlayControlDataClass();
jmethodID GetOverlayControlDataConstructor();
jfieldID GetOverlayControlDataIdField();
jfieldID GetOverlayControlDataEnabledField();
jfieldID GetOverlayControlDataLandscapePositionField();
jfieldID GetOverlayControlDataPortraitPositionField();
jfieldID GetOverlayControlDataFoldablePositionField();
jclass GetDoubleClass();
jmethodID GetDoubleConstructor();
jfieldID GetDoubleValueField();
} // namespace IDCache } // namespace IDCache

View File

@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
Settings::values.disabled_addons[program_id] = disabled_addons; Settings::values.disabled_addons[program_id] = disabled_addons;
} }
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
jobject obj) {
jobjectArray joverlayControlDataArray =
env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
IDCache::GetOverlayControlDataClass(), nullptr);
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
const auto& control_data = AndroidSettings::values.overlay_control_data[i];
jobject jlandscapePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.landscape_position.first),
ToJDouble(env, control_data.landscape_position.second));
jobject jportraitPosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.portrait_position.first),
ToJDouble(env, control_data.portrait_position.second));
jobject jfoldablePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
ToJDouble(env, control_data.foldable_position.first),
ToJDouble(env, control_data.foldable_position.second));
jobject jcontrolData = env->NewObject(
IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
}
return joverlayControlDataArray;
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
AndroidSettings::values.overlay_control_data.clear();
int size = env->GetArrayLength(joverlayControlDataArray);
if (size == 0) {
return;
}
for (int i = 0; i < size; ++i) {
jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
jstring jidString = static_cast<jstring>(
env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
bool enabled = static_cast<bool>(env->GetBooleanField(
joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
jobject jlandscapePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
std::pair<double, double> landscape_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
GetJDouble(env,
env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
jobject jportraitPosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
std::pair<double, double> portrait_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
jobject jfoldablePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
std::pair<double, double> foldable_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
GetJString(env, jidString), enabled, landscape_position, portrait_position,
foldable_position});
}
}
} // extern "C" } // extern "C"

View File

@ -24,7 +24,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:gravity="center_vertical" android:layout_gravity="center_vertical"
android:orientation="vertical" android:orientation="vertical"
android:layout_weight="1"> android:layout_weight="1">

View File

@ -38,6 +38,11 @@
android:title="@string/emulation_haptics" android:title="@string/emulation_haptics"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/menu_touchscreen"
android:title="@string/touchscreen"
android:checkable="true" />
<item <item
android:id="@+id/menu_reset_overlay" android:id="@+id/menu_reset_overlay"
android:title="@string/emulation_touch_overlay_reset" /> android:title="@string/emulation_touch_overlay_reset" />

View File

@ -212,19 +212,19 @@
<item>B</item> <item>B</item>
<item>X</item> <item>X</item>
<item>Y</item> <item>Y</item>
<item>+</item>
<item>-</item>
<item>@string/gamepad_home</item>
<item>@string/gamepad_screenshot</item>
<item>L</item> <item>L</item>
<item>R</item> <item>R</item>
<item>ZL</item> <item>ZL</item>
<item>ZR</item> <item>ZR</item>
<item>+</item>
<item>-</item>
<item>@string/gamepad_d_pad</item>
<item>@string/gamepad_left_stick</item> <item>@string/gamepad_left_stick</item>
<item>@string/gamepad_right_stick</item> <item>@string/gamepad_right_stick</item>
<item>L3</item> <item>L3</item>
<item>R3</item> <item>R3</item>
<item>@string/gamepad_home</item> <item>@string/gamepad_d_pad</item>
<item>@string/gamepad_screenshot</item>
</string-array> </string-array>
<string-array name="themeEntries"> <string-array name="themeEntries">
@ -267,4 +267,21 @@
<item>3</item> <item>3</item>
</integer-array> </integer-array>
<string-array name="anisoEntries">
<item>@string/auto</item>
<item>@string/slider_default</item>
<item>@string/multiplier_two</item>
<item>@string/multiplier_four</item>
<item>@string/multiplier_eight</item>
<item>@string/multiplier_sixteen</item>
</string-array>
<integer-array name="anisoValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>
</resources> </resources>

View File

@ -3,111 +3,111 @@
<integer name="grid_columns">1</integer> <integer name="grid_columns">1</integer>
<!-- Default SWITCH landscape layout --> <!-- Default SWITCH landscape layout -->
<integer name="SWITCH_BUTTON_A_X">760</integer> <integer name="BUTTON_A_X">760</integer>
<integer name="SWITCH_BUTTON_A_Y">790</integer> <integer name="BUTTON_A_Y">790</integer>
<integer name="SWITCH_BUTTON_B_X">710</integer> <integer name="BUTTON_B_X">710</integer>
<integer name="SWITCH_BUTTON_B_Y">900</integer> <integer name="BUTTON_B_Y">900</integer>
<integer name="SWITCH_BUTTON_X_X">710</integer> <integer name="BUTTON_X_X">710</integer>
<integer name="SWITCH_BUTTON_X_Y">680</integer> <integer name="BUTTON_X_Y">680</integer>
<integer name="SWITCH_BUTTON_Y_X">660</integer> <integer name="BUTTON_Y_X">660</integer>
<integer name="SWITCH_BUTTON_Y_Y">790</integer> <integer name="BUTTON_Y_Y">790</integer>
<integer name="SWITCH_STICK_L_X">100</integer> <integer name="BUTTON_PLUS_X">540</integer>
<integer name="SWITCH_STICK_L_Y">670</integer> <integer name="BUTTON_PLUS_Y">950</integer>
<integer name="SWITCH_STICK_R_X">900</integer> <integer name="BUTTON_MINUS_X">460</integer>
<integer name="SWITCH_STICK_R_Y">670</integer> <integer name="BUTTON_MINUS_Y">950</integer>
<integer name="SWITCH_TRIGGER_L_X">70</integer> <integer name="BUTTON_HOME_X">600</integer>
<integer name="SWITCH_TRIGGER_L_Y">220</integer> <integer name="BUTTON_HOME_Y">950</integer>
<integer name="SWITCH_TRIGGER_R_X">930</integer> <integer name="BUTTON_CAPTURE_X">400</integer>
<integer name="SWITCH_TRIGGER_R_Y">220</integer> <integer name="BUTTON_CAPTURE_Y">950</integer>
<integer name="SWITCH_TRIGGER_ZL_X">70</integer> <integer name="BUTTON_L_X">70</integer>
<integer name="SWITCH_TRIGGER_ZL_Y">90</integer> <integer name="BUTTON_L_Y">220</integer>
<integer name="SWITCH_TRIGGER_ZR_X">930</integer> <integer name="BUTTON_R_X">930</integer>
<integer name="SWITCH_TRIGGER_ZR_Y">90</integer> <integer name="BUTTON_R_Y">220</integer>
<integer name="SWITCH_BUTTON_MINUS_X">460</integer> <integer name="BUTTON_ZL_X">70</integer>
<integer name="SWITCH_BUTTON_MINUS_Y">950</integer> <integer name="BUTTON_ZL_Y">90</integer>
<integer name="SWITCH_BUTTON_PLUS_X">540</integer> <integer name="BUTTON_ZR_X">930</integer>
<integer name="SWITCH_BUTTON_PLUS_Y">950</integer> <integer name="BUTTON_ZR_Y">90</integer>
<integer name="SWITCH_BUTTON_HOME_X">600</integer> <integer name="BUTTON_STICK_L_X">870</integer>
<integer name="SWITCH_BUTTON_HOME_Y">950</integer> <integer name="BUTTON_STICK_L_Y">400</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X">400</integer> <integer name="BUTTON_STICK_R_X">960</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> <integer name="BUTTON_STICK_R_Y">430</integer>
<integer name="SWITCH_BUTTON_DPAD_X">260</integer> <integer name="STICK_L_X">100</integer>
<integer name="SWITCH_BUTTON_DPAD_Y">790</integer> <integer name="STICK_L_Y">670</integer>
<integer name="SWITCH_BUTTON_STICK_L_X">870</integer> <integer name="STICK_R_X">900</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y">400</integer> <integer name="STICK_R_Y">670</integer>
<integer name="SWITCH_BUTTON_STICK_R_X">960</integer> <integer name="COMBINED_DPAD_X">260</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y">430</integer> <integer name="COMBINED_DPAD_Y">790</integer>
<!-- Default SWITCH portrait layout --> <!-- Default SWITCH portrait layout -->
<integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> <integer name="BUTTON_A_X_PORTRAIT">840</integer>
<integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer> <integer name="BUTTON_A_Y_PORTRAIT">840</integer>
<integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer> <integer name="BUTTON_B_X_PORTRAIT">740</integer>
<integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer> <integer name="BUTTON_B_Y_PORTRAIT">880</integer>
<integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer> <integer name="BUTTON_X_X_PORTRAIT">740</integer>
<integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer> <integer name="BUTTON_X_Y_PORTRAIT">800</integer>
<integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer> <integer name="BUTTON_Y_X_PORTRAIT">640</integer>
<integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer> <integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
<integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer> <integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
<integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer> <integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
<integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer> <integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
<integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer> <integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
<integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer> <integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
<integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer> <integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
<integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer> <integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
<integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer> <integer name="BUTTON_L_X_PORTRAIT">140</integer>
<integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer> <integer name="BUTTON_L_Y_PORTRAIT">260</integer>
<integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer> <integer name="BUTTON_R_X_PORTRAIT">860</integer>
<integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer> <integer name="BUTTON_R_Y_PORTRAIT">260</integer>
<integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer> <integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
<integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer> <integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
<integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer> <integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
<integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer> <integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
<integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer> <integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
<integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer> <integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
<integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer> <integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer> <integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> <integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
<integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> <integer name="STICK_L_X_PORTRAIT">180</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> <integer name="STICK_L_Y_PORTRAIT">660</integer>
<integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer> <integer name="STICK_R_X_PORTRAIT">820</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer> <integer name="STICK_R_Y_PORTRAIT">660</integer>
<integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer> <integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer> <integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
<!-- Default SWITCH foldable layout --> <!-- Default SWITCH foldable layout -->
<integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> <integer name="BUTTON_A_X_FOLDABLE">840</integer>
<integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer> <integer name="BUTTON_A_Y_FOLDABLE">390</integer>
<integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer> <integer name="BUTTON_B_X_FOLDABLE">740</integer>
<integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer> <integer name="BUTTON_B_Y_FOLDABLE">430</integer>
<integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer> <integer name="BUTTON_X_X_FOLDABLE">740</integer>
<integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer> <integer name="BUTTON_X_Y_FOLDABLE">350</integer>
<integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer> <integer name="BUTTON_Y_X_FOLDABLE">640</integer>
<integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer> <integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
<integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer> <integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
<integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer> <integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
<integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer> <integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
<integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer> <integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
<integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer> <integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
<integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer> <integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
<integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer> <integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
<integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer> <integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
<integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer> <integer name="BUTTON_L_X_FOLDABLE">140</integer>
<integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer> <integer name="BUTTON_L_Y_FOLDABLE">130</integer>
<integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer> <integer name="BUTTON_R_X_FOLDABLE">860</integer>
<integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer> <integer name="BUTTON_R_Y_FOLDABLE">130</integer>
<integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer> <integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
<integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer> <integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
<integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer> <integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
<integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer> <integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
<integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer> <integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
<integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer> <integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
<integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer> <integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
<integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> <integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
<integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> <integer name="STICK_L_X_FOLDABLE">180</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> <integer name="STICK_L_Y_FOLDABLE">250</integer>
<integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer> <integer name="STICK_R_X_FOLDABLE">820</integer>
<integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer> <integer name="STICK_R_Y_FOLDABLE">250</integer>
<integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer> <integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
<integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer> <integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
</resources> </resources>

View File

@ -225,6 +225,8 @@
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="use_disk_shader_cache">Disk shader cache</string> <string name="use_disk_shader_cache">Disk shader cache</string>
<string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
<string name="anisotropic_filtering">Anisotropic filtering</string>
<string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
<!-- Debug settings strings --> <!-- Debug settings strings -->
<string name="cpu">CPU</string> <string name="cpu">CPU</string>
@ -364,6 +366,7 @@
<string name="emulation_pause">Pause emulation</string> <string name="emulation_pause">Pause emulation</string>
<string name="emulation_unpause">Unpause emulation</string> <string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string> <string name="emulation_input_overlay">Overlay options</string>
<string name="touchscreen">Touchscreen</string>
<string name="load_settings">Loading settings…</string> <string name="load_settings">Loading settings…</string>
@ -506,6 +509,12 @@
<string name="oboe">oboe</string> <string name="oboe">oboe</string>
<string name="cubeb">cubeb</string> <string name="cubeb">cubeb</string>
<!-- Anisotropic filtering options -->
<string name="multiplier_two">2x</string>
<string name="multiplier_four">4x</string>
<string name="multiplier_eight">8x</string>
<string name="multiplier_sixteen">16x</string>
<!-- Black backgrounds theme --> <!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Black backgrounds</string> <string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>

View File

@ -5,7 +5,7 @@
plugins { plugins {
id("com.android.application") version "8.1.2" apply false id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.1.2" apply false id("com.android.library") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.21" apply false id("org.jetbrains.kotlin.android") version "1.9.20" apply false
} }
tasks.register("clean").configure { tasks.register("clean").configure {

View File

@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms};
DeviceSession::DeviceSession(Core::System& system_) DeviceSession::DeviceSession(Core::System& system_)
: system{system_}, thread_event{Core::Timing::CreateEvent( : system{system_}, thread_event{Core::Timing::CreateEvent(
"AudioOutSampleTick", "AudioOutSampleTick",
[this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { [this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
return ThreadFunc();
})} {}
DeviceSession::~DeviceSession() { DeviceSession::~DeviceSession() {
Finalize(); Finalize();

View File

@ -64,6 +64,8 @@ add_library(common STATIC
fs/path_util.cpp fs/path_util.cpp
fs/path_util.h fs/path_util.h
hash.h hash.h
heap_tracker.cpp
heap_tracker.h
hex_util.cpp hex_util.cpp
hex_util.h hex_util.h
host_memory.cpp host_memory.cpp

View File

@ -3,16 +3,19 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/logging/backend.h"
#include "common/settings.h" #include "common/settings.h"
void assert_fail_impl() { void assert_fail_impl() {
if (Settings::values.use_debug_asserts) { if (Settings::values.use_debug_asserts) {
Common::Log::Stop();
Crash(); Crash();
} }
} }
[[noreturn]] void unreachable_impl() { [[noreturn]] void unreachable_impl() {
Common::Log::Stop();
Crash(); Crash();
throw std::runtime_error("Unreachable code"); throw std::runtime_error("Unreachable code");
} }

281
src/common/heap_tracker.cpp Normal file
View File

@ -0,0 +1,281 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <vector>
#include "common/heap_tracker.h"
#include "common/logging/log.h"
namespace Common {
namespace {
s64 GetMaxPermissibleResidentMapCount() {
// Default value.
s64 value = 65530;
// Try to read how many mappings we can make.
std::ifstream s("/proc/sys/vm/max_map_count");
s >> value;
// Print, for debug.
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
// Allow 20000 maps for other code and to account for split inaccuracy.
return std::max<s64>(value - 20000, 0);
}
} // namespace
HeapTracker::HeapTracker(Common::HostMemory& buffer)
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
HeapTracker::~HeapTracker() = default;
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
MemoryPermission perm, bool is_separate_heap) {
// When mapping other memory, map pages immediately.
if (!is_separate_heap) {
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
return;
}
{
// We are mapping part of a separate heap.
std::scoped_lock lk{m_lock};
auto* const map = new SeparateHeapMap{
.vaddr = virtual_offset,
.paddr = host_offset,
.size = length,
.tick = m_tick++,
.perm = perm,
.is_resident = false,
};
// Insert into mappings.
m_map_count++;
m_mappings.insert(*map);
}
// Finally, map.
this->DeferredMapSeparateHeap(virtual_offset);
}
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
// If this is a separate heap...
if (is_separate_heap) {
std::scoped_lock lk{m_lock};
const SeparateHeapMap key{
.vaddr = virtual_offset,
};
// Split at the boundaries of the region we are removing.
this->SplitHeapMapLocked(virtual_offset);
this->SplitHeapMapLocked(virtual_offset + size);
// Erase all mappings in range.
auto it = m_mappings.find(key);
while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
// Get underlying item.
auto* const item = std::addressof(*it);
// If resident, erase from resident map.
if (item->is_resident) {
ASSERT(--m_resident_map_count >= 0);
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
}
// Erase from map.
ASSERT(--m_map_count >= 0);
it = m_mappings.erase(it);
// Free the item.
delete item;
}
}
// Unmap pages.
m_buffer.Unmap(virtual_offset, size, false);
}
void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
// Ensure no rebuild occurs while reprotecting.
std::shared_lock lk{m_rebuild_lock};
// Split at the boundaries of the region we are reprotecting.
this->SplitHeapMap(virtual_offset, size);
// Declare tracking variables.
const VAddr end = virtual_offset + size;
VAddr cur = virtual_offset;
while (cur < end) {
VAddr next = cur;
bool should_protect = false;
{
std::scoped_lock lk2{m_lock};
const SeparateHeapMap key{
.vaddr = next,
};
// Try to get the next mapping corresponding to this address.
const auto it = m_mappings.nfind(key);
if (it == m_mappings.end()) {
// There are no separate heap mappings remaining.
next = end;
should_protect = true;
} else if (it->vaddr == cur) {
// We are in range.
// Update permission bits.
it->perm = perm;
// Determine next address and whether we should protect.
next = cur + it->size;
should_protect = it->is_resident;
} else /* if (it->vaddr > cur) */ {
// We weren't in range, but there is a block coming up that will be.
next = it->vaddr;
should_protect = true;
}
}
// Clamp to end.
next = std::min(next, end);
// Reprotect, if we need to.
if (should_protect) {
m_buffer.Protect(cur, next - cur, perm);
}
// Advance.
cur = next;
}
}
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
if (m_buffer.IsInVirtualRange(fault_address)) {
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
}
return false;
}
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
bool rebuild_required = false;
{
std::scoped_lock lk{m_lock};
// Check to ensure this was a non-resident separate heap mapping.
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
if (it == m_mappings.end() || it->is_resident) {
return false;
}
// Update tick before possible rebuild.
it->tick = m_tick++;
// Check if we need to rebuild.
if (m_resident_map_count > m_max_resident_map_count) {
rebuild_required = true;
}
// Map the area.
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
// This map is now resident.
it->is_resident = true;
m_resident_map_count++;
m_resident_mappings.insert(*it);
}
if (rebuild_required) {
// A rebuild was required, so perform it now.
this->RebuildSeparateHeapAddressSpace();
}
return true;
}
void HeapTracker::RebuildSeparateHeapAddressSpace() {
std::scoped_lock lk{m_rebuild_lock, m_lock};
ASSERT(!m_resident_mappings.empty());
// Dump half of the mappings.
//
// Despite being worse in theory, this has proven to be better in practice than more
// regularly dumping a smaller amount, because it significantly reduces average case
// lock contention.
const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
const size_t evict_count = m_resident_map_count - desired_count;
auto it = m_resident_mappings.begin();
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
// Unmark and unmap.
it->is_resident = false;
m_buffer.Unmap(it->vaddr, it->size, false);
// Advance.
ASSERT(--m_resident_map_count >= 0);
it = m_resident_mappings.erase(it);
}
}
void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
std::scoped_lock lk{m_lock};
this->SplitHeapMapLocked(offset);
this->SplitHeapMapLocked(offset + size);
}
void HeapTracker::SplitHeapMapLocked(VAddr offset) {
const auto it = this->GetNearestHeapMapLocked(offset);
if (it == m_mappings.end() || it->vaddr == offset) {
// Not contained or no split required.
return;
}
// Cache the original values.
auto* const left = std::addressof(*it);
const size_t orig_size = left->size;
// Adjust the left map.
const size_t left_size = offset - left->vaddr;
left->size = left_size;
// Create the new right map.
auto* const right = new SeparateHeapMap{
.vaddr = left->vaddr + left_size,
.paddr = left->paddr + left_size,
.size = orig_size - left_size,
.tick = left->tick,
.perm = left->perm,
.is_resident = left->is_resident,
};
// Insert the new right map.
m_map_count++;
m_mappings.insert(*right);
// If resident, also insert into resident map.
if (right->is_resident) {
m_resident_map_count++;
m_resident_mappings.insert(*right);
}
}
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
const SeparateHeapMap key{
.vaddr = offset,
};
return m_mappings.find(key);
}
} // namespace Common

98
src/common/heap_tracker.h Normal file
View File

@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <mutex>
#include <set>
#include <shared_mutex>
#include "common/host_memory.h"
#include "common/intrusive_red_black_tree.h"
namespace Common {
struct SeparateHeapMap {
Common::IntrusiveRedBlackTreeNode addr_node{};
Common::IntrusiveRedBlackTreeNode tick_node{};
VAddr vaddr{};
PAddr paddr{};
size_t size{};
size_t tick{};
MemoryPermission perm{};
bool is_resident{};
};
struct SeparateHeapMapAddrComparator {
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
if (lhs.vaddr < rhs.vaddr) {
return -1;
} else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
return 0;
} else {
return 1;
}
}
};
struct SeparateHeapMapTickComparator {
static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
if (lhs.tick < rhs.tick) {
return -1;
} else if (lhs.tick > rhs.tick) {
return 1;
} else {
return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
}
}
};
class HeapTracker {
public:
explicit HeapTracker(Common::HostMemory& buffer);
~HeapTracker();
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
bool is_separate_heap);
void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
u8* VirtualBasePointer() {
return m_buffer.VirtualBasePointer();
}
bool DeferredMapSeparateHeap(u8* fault_address);
bool DeferredMapSeparateHeap(size_t virtual_offset);
private:
using AddrTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
using TickTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
AddrTree m_mappings{};
TickTree m_resident_mappings{};
private:
void SplitHeapMap(VAddr offset, size_t size);
void SplitHeapMapLocked(VAddr offset);
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
void RebuildSeparateHeapAddressSpace();
private:
Common::HostMemory& m_buffer;
const s64 m_max_resident_map_count;
std::shared_mutex m_rebuild_lock{};
std::mutex m_lock{};
s64 m_map_count{};
s64 m_resident_map_count{};
size_t m_tick{};
};
} // namespace Common

View File

@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
MemoryPermission perms) { MemoryPermission perms, bool separate_heap) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
} }
void HostMemory::Unmap(size_t virtual_offset, size_t length) { void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
impl->Unmap(virtual_offset + virtual_base_offset, length); impl->Unmap(virtual_offset + virtual_base_offset, length);
} }
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
bool execute) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
const bool read = True(perm & MemoryPermission::Read);
const bool write = True(perm & MemoryPermission::Write);
const bool execute = True(perm & MemoryPermission::Execute);
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
} }

View File

@ -40,11 +40,12 @@ public:
HostMemory(HostMemory&& other) noexcept; HostMemory(HostMemory&& other) noexcept;
HostMemory& operator=(HostMemory&& other) noexcept; HostMemory& operator=(HostMemory&& other) noexcept;
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
bool separate_heap);
void Unmap(size_t virtual_offset, size_t length); void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
void EnableDirectMappedAddress(); void EnableDirectMappedAddress();
@ -64,6 +65,10 @@ public:
return virtual_base; return virtual_base;
} }
bool IsInVirtualRange(void* address) const noexcept {
return address >= virtual_base && address < virtual_base + virtual_size;
}
private: private:
size_t backing_size{}; size_t backing_size{};
size_t virtual_size{}; size_t virtual_size{};

View File

@ -208,6 +208,10 @@ public:
instance->StartBackendThread(); instance->StartBackendThread();
} }
static void Stop() {
instance->StopBackendThread();
}
Impl(const Impl&) = delete; Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete; Impl& operator=(const Impl&) = delete;
@ -259,6 +263,15 @@ private:
}); });
} }
void StopBackendThread() {
backend_thread.request_stop();
if (backend_thread.joinable()) {
backend_thread.join();
}
ForEachBackend([](Backend& backend) { backend.Flush(); });
}
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string&& message) const { const char* function, std::string&& message) const {
using std::chrono::duration_cast; using std::chrono::duration_cast;
@ -313,6 +326,10 @@ void Start() {
Impl::Start(); Impl::Start();
} }
void Stop() {
Impl::Stop();
}
void DisableLoggingInTests() { void DisableLoggingInTests() {
initialization_in_progress_suppress_logging = true; initialization_in_progress_suppress_logging = true;
} }

View File

@ -14,6 +14,9 @@ void Initialize();
void Start(); void Start();
/// Explicitly stops the logger thread and flushes the buffers
void Stop();
void DisableLoggingInTests(); void DisableLoggingInTests();
/** /**

View File

@ -103,7 +103,7 @@ private:
// Having them on the same cache-line would result in false-sharing between them. // Having them on the same cache-line would result in false-sharing between them.
// TODO: Remove this ifdef whenever clang and GCC support // TODO: Remove this ifdef whenever clang and GCC support
// std::hardware_destructive_interference_size. // std::hardware_destructive_interference_size.
#if defined(_MSC_VER) && _MSC_VER >= 1911 #ifdef __cpp_lib_hardware_interference_size
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
#else #else

View File

@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
case Category::CpuDebug: case Category::CpuDebug:
case Category::CpuUnsafe: case Category::CpuUnsafe:
return "Cpu"; return "Cpu";
case Category::Overlay:
return "Overlay";
case Category::Renderer: case Category::Renderer:
case Category::RendererAdvanced: case Category::RendererAdvanced:
case Category::RendererDebug: case Category::RendererDebug:

View File

@ -18,6 +18,7 @@ enum class Category : u32 {
Cpu, Cpu,
CpuDebug, CpuDebug,
CpuUnsafe, CpuUnsafe,
Overlay,
Renderer, Renderer,
RendererAdvanced, RendererAdvanced,
RendererDebug, RendererDebug,

View File

@ -978,6 +978,7 @@ endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.cpp
arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h arm/dynarmic/arm_dynarmic_64.h
@ -987,6 +988,8 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
arm/dynarmic/dynarmic_cp15.h arm/dynarmic/dynarmic_cp15.h
arm/dynarmic/dynarmic_exclusive_monitor.cpp arm/dynarmic/dynarmic_exclusive_monitor.cpp
arm/dynarmic/dynarmic_exclusive_monitor.h arm/dynarmic/dynarmic_exclusive_monitor.h
hle/service/jit/jit_code_memory.cpp
hle/service/jit/jit_code_memory.h
hle/service/jit/jit_context.cpp hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h hle/service/jit/jit_context.h
hle/service/jit/jit.cpp hle/service/jit/jit.cpp

View File

@ -9,7 +9,7 @@
namespace Core { namespace Core {
void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const { void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
Kernel::Svc::ThreadContext ctx; Kernel::Svc::ThreadContext ctx;
this->GetContext(ctx); this->GetContext(ctx);

View File

@ -95,7 +95,7 @@ public:
virtual void SignalInterrupt(Kernel::KThread* thread) = 0; virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
// Stack trace generation. // Stack trace generation.
void LogBacktrace(const Kernel::KProcess* process) const; void LogBacktrace(Kernel::KProcess* process) const;
// Debug functionality. // Debug functionality.
virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;

View File

@ -79,7 +79,7 @@ constexpr std::array<u64, 2> SegmentBases{
0x7100000000ULL, 0x7100000000ULL,
}; };
void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) { void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
auto modules = FindModules(process); auto modules = FindModules(process);
const bool is_64 = process->Is64Bit(); const bool is_64 = process->Is64Bit();
@ -118,7 +118,7 @@ void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<Backtrace
} }
} }
std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process, std::vector<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) { const Kernel::Svc::ThreadContext& ctx) {
std::vector<BacktraceEntry> out; std::vector<BacktraceEntry> out;
auto& memory = process->GetMemory(); auto& memory = process->GetMemory();
@ -144,7 +144,7 @@ std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
return out; return out;
} }
std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process, std::vector<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) { const Kernel::Svc::ThreadContext& ctx) {
std::vector<BacktraceEntry> out; std::vector<BacktraceEntry> out;
auto& memory = process->GetMemory(); auto& memory = process->GetMemory();
@ -173,7 +173,7 @@ std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
} // namespace } // namespace
std::optional<std::string> GetThreadName(const Kernel::KThread* thread) { std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
const auto* process = thread->GetOwnerProcess(); auto* process = thread->GetOwnerProcess();
if (process->Is64Bit()) { if (process->Is64Bit()) {
return GetNameFromThreadType64(process->GetMemory(), *thread); return GetNameFromThreadType64(process->GetMemory(), *thread);
} else { } else {
@ -248,7 +248,7 @@ Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
return cur_addr - 1; return cur_addr - 1;
} }
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { Loader::AppLoader::Modules FindModules(Kernel::KProcess* process) {
Loader::AppLoader::Modules modules; Loader::AppLoader::Modules modules;
auto& page_table = process->GetPageTable(); auto& page_table = process->GetPageTable();
@ -312,7 +312,7 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
return modules; return modules;
} }
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process) {
// Do we have any loaded executable sections? // Do we have any loaded executable sections?
auto modules = FindModules(process); auto modules = FindModules(process);
@ -337,7 +337,7 @@ void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 addres
} }
} }
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx) { const Kernel::Svc::ThreadContext& ctx) {
if (process->Is64Bit()) { if (process->Is64Bit()) {
return GetAArch64Backtrace(process, ctx); return GetAArch64Backtrace(process, ctx);

View File

@ -14,9 +14,9 @@ std::optional<std::string> GetThreadName(const Kernel::KThread* thread);
std::string_view GetThreadWaitReason(const Kernel::KThread* thread); std::string_view GetThreadWaitReason(const Kernel::KThread* thread);
std::string GetThreadState(const Kernel::KThread* thread); std::string GetThreadState(const Kernel::KThread* thread);
Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process); Loader::AppLoader::Modules FindModules(Kernel::KProcess* process);
Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base);
Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process); Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process);
void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size); void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size);
@ -28,7 +28,7 @@ struct BacktraceEntry {
std::string name; std::string name;
}; };
std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
const Kernel::Svc::ThreadContext& ctx); const Kernel::Svc::ThreadContext& ctx);
std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread); std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread);

View File

@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef __linux__
#include "common/signal_chain.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
namespace Core {
namespace {
thread_local Core::Memory::Memory* g_current_memory{};
std::once_flag g_registered{};
struct sigaction g_old_segv {};
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
return;
}
return g_old_segv.sa_sigaction(sig, info, ctx);
}
} // namespace
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
g_current_memory = std::addressof(process->GetMemory());
}
ScopedJitExecution::~ScopedJitExecution() {
g_current_memory = nullptr;
}
void ScopedJitExecution::RegisterHandler() {
std::call_once(g_registered, [] {
struct sigaction sa {};
sa.sa_sigaction = &HandleSigSegv;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
});
}
} // namespace Core
#endif

View File

@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
return static_cast<HaltReason>(hr); return static_cast<HaltReason>(hr);
} }
#ifdef __linux__
class ScopedJitExecution {
public:
explicit ScopedJitExecution(Kernel::KProcess* process);
~ScopedJitExecution();
static void RegisterHandler();
};
#else
class ScopedJitExecution {
public:
explicit ScopedJitExecution(Kernel::KProcess* process) {}
~ScopedJitExecution() {}
static void RegisterHandler() {}
};
#endif
} // namespace Core } // namespace Core

View File

@ -15,7 +15,7 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process) explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process)
: m_parent{parent}, m_memory(process->GetMemory()), : m_parent{parent}, m_memory(process->GetMemory()),
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
m_check_memory_access{m_debugger_enabled || m_check_memory_access{m_debugger_enabled ||
@ -169,7 +169,7 @@ public:
ArmDynarmic32& m_parent; ArmDynarmic32& m_parent;
Core::Memory::Memory& m_memory; Core::Memory::Memory& m_memory;
const Kernel::KProcess* m_process{}; Kernel::KProcess* m_process{};
const bool m_debugger_enabled{}; const bool m_debugger_enabled{};
const bool m_check_memory_access{}; const bool m_check_memory_access{};
static constexpr u64 MinimumRunCycles = 10000U; static constexpr u64 MinimumRunCycles = 10000U;
@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
} }
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run()); return TranslateHaltReason(m_jit->Run());
} }
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step()); return TranslateHaltReason(m_jit->Step());
} }
@ -370,13 +374,14 @@ void ArmDynarmic32::RewindBreakpointInstruction() {
this->SetContext(m_breakpoint_context); this->SetContext(m_breakpoint_context);
} }
ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)), m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)),
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
m_jit = MakeJit(&page_table_impl); m_jit = MakeJit(&page_table_impl);
ScopedJitExecution::RegisterHandler();
} }
ArmDynarmic32::~ArmDynarmic32() = default; ArmDynarmic32::~ArmDynarmic32() = default;

View File

@ -20,7 +20,7 @@ class System;
class ArmDynarmic32 final : public ArmInterface { class ArmDynarmic32 final : public ArmInterface {
public: public:
ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ArmDynarmic32() override; ~ArmDynarmic32() override;

View File

@ -15,7 +15,7 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public: public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process) explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process)
: m_parent{parent}, m_memory(process->GetMemory()), : m_parent{parent}, m_memory(process->GetMemory()),
m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
m_check_memory_access{m_debugger_enabled || m_check_memory_access{m_debugger_enabled ||
@ -216,7 +216,7 @@ public:
Core::Memory::Memory& m_memory; Core::Memory::Memory& m_memory;
u64 m_tpidrro_el0{}; u64 m_tpidrro_el0{};
u64 m_tpidr_el0{}; u64 m_tpidr_el0{};
const Kernel::KProcess* m_process{}; Kernel::KProcess* m_process{};
const bool m_debugger_enabled{}; const bool m_debugger_enabled{};
const bool m_check_memory_access{}; const bool m_check_memory_access{};
static constexpr u64 MinimumRunCycles = 10000U; static constexpr u64 MinimumRunCycles = 10000U;
@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
} }
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Run()); return TranslateHaltReason(m_jit->Run());
} }
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
ScopedJitExecution sj(thread->GetOwnerProcess());
m_jit->ClearExclusiveState(); m_jit->ClearExclusiveState();
return TranslateHaltReason(m_jit->Step()); return TranslateHaltReason(m_jit->Step());
} }
@ -399,13 +403,14 @@ void ArmDynarmic64::RewindBreakpointInstruction() {
this->SetContext(m_breakpoint_context); this->SetContext(m_breakpoint_context);
} }
ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
: ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} { m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} {
auto& page_table = process->GetPageTable().GetBasePageTable(); auto& page_table = process->GetPageTable().GetBasePageTable();
auto& page_table_impl = page_table.GetImpl(); auto& page_table_impl = page_table.GetImpl();
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
ScopedJitExecution::RegisterHandler();
} }
ArmDynarmic64::~ArmDynarmic64() = default; ArmDynarmic64::~ArmDynarmic64() = default;

View File

@ -25,7 +25,7 @@ class System;
class ArmDynarmic64 final : public ArmInterface { class ArmDynarmic64 final : public ArmInterface {
public: public:
ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ArmDynarmic64() override; ~ArmDynarmic64() override;

View File

@ -28,7 +28,6 @@
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
@ -130,11 +129,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{}, reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
time_manager{system}, gpu_dirty_memory_write_manager{} {
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
}
void Initialize(System& system) { void Initialize(System& system) {
device_memory = std::make_unique<Core::DeviceMemory>(); device_memory = std::make_unique<Core::DeviceMemory>();
@ -241,17 +237,17 @@ struct System::Impl {
debugger = std::make_unique<Debugger>(system, port); debugger = std::make_unique<Debugger>(system, port);
} }
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { void InitializeKernel(System& system) {
LOG_DEBUG(Core, "initialized OK"); LOG_DEBUG(Core, "initialized OK");
// Setting changes may require a full system reinitialization (e.g., disabling multicore). // Setting changes may require a full system reinitialization (e.g., disabling multicore).
ReinitializeIfNecessary(system); ReinitializeIfNecessary(system);
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
kernel.Initialize(); kernel.Initialize();
cpu_manager.Initialize(); cpu_manager.Initialize();
}
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
/// Reset all glue registrations /// Reset all glue registrations
arp_manager.ResetAll(); arp_manager.ResetAll();
@ -300,17 +296,9 @@ struct System::Impl {
return SystemResultStatus::ErrorGetLoader; return SystemResultStatus::ErrorGetLoader;
} }
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; InitializeKernel(system);
if (init_result != SystemResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
ShutdownMainProcess();
return init_result;
}
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); // Create the application process.
// Create the process.
auto main_process = Kernel::KProcess::Create(system.Kernel()); auto main_process = Kernel::KProcess::Create(system.Kernel());
Kernel::KProcess::Register(system.Kernel(), main_process); Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.AppendNewProcess(main_process); kernel.AppendNewProcess(main_process);
@ -323,7 +311,18 @@ struct System::Impl {
return static_cast<SystemResultStatus>( return static_cast<SystemResultStatus>(
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
} }
// Set up the rest of the system.
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
if (init_result != SystemResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
ShutdownMainProcess();
return init_result;
}
AddGlueRegistrationForProcess(*app_loader, *main_process); AddGlueRegistrationForProcess(*app_loader, *main_process);
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
// Initialize cheat engine // Initialize cheat engine
if (cheat_engine) { if (cheat_engine) {
@ -426,7 +425,6 @@ struct System::Impl {
cpu_manager.Shutdown(); cpu_manager.Shutdown();
debugger.reset(); debugger.reset();
kernel.Shutdown(); kernel.Shutdown();
memory.Reset();
Network::RestartSocketOperations(); Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) { if (auto room_member = room_network.GetRoomMember().lock()) {
@ -507,7 +505,6 @@ struct System::Impl {
std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
std::unique_ptr<Core::DeviceMemory> device_memory; std::unique_ptr<Core::DeviceMemory> device_memory;
std::unique_ptr<AudioCore::AudioCore> audio_core; std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core; Core::HID::HIDCore hid_core;
Network::RoomNetwork room_network; Network::RoomNetwork room_network;
@ -567,9 +564,6 @@ struct System::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
gpu_dirty_memory_write_manager{};
std::deque<std::vector<u8>> user_channel; std::deque<std::vector<u8>> user_channel;
}; };
@ -652,29 +646,12 @@ void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index); impl->kernel.PrepareReschedule(core_index);
} }
Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
const std::size_t core = impl->kernel.GetCurrentHostThreadID();
return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
? core
: Core::Hardware::NUM_CPU_CORES - 1];
}
/// Provides a constant reference to the current gou dirty memory manager.
const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
const std::size_t core = impl->kernel.GetCurrentHostThreadID();
return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
? core
: Core::Hardware::NUM_CPU_CORES - 1];
}
size_t System::GetCurrentHostThreadID() const { size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID(); return impl->kernel.GetCurrentHostThreadID();
} }
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
for (auto& manager : impl->gpu_dirty_memory_write_manager) { return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
manager.Gather(callback);
}
} }
PerfStatsResults System::GetAndResetPerfStats() { PerfStatsResults System::GetAndResetPerfStats() {
@ -723,20 +700,12 @@ const Kernel::KProcess* System::ApplicationProcess() const {
return impl->kernel.ApplicationProcess(); return impl->kernel.ApplicationProcess();
} }
ExclusiveMonitor& System::Monitor() {
return impl->kernel.GetExclusiveMonitor();
}
const ExclusiveMonitor& System::Monitor() const {
return impl->kernel.GetExclusiveMonitor();
}
Memory::Memory& System::ApplicationMemory() { Memory::Memory& System::ApplicationMemory() {
return impl->memory; return impl->kernel.ApplicationProcess()->GetMemory();
} }
const Core::Memory::Memory& System::ApplicationMemory() const { const Core::Memory::Memory& System::ApplicationMemory() const {
return impl->memory; return impl->kernel.ApplicationProcess()->GetMemory();
} }
Tegra::GPU& System::GPU() { Tegra::GPU& System::GPU() {

View File

@ -116,7 +116,6 @@ class CpuManager;
class Debugger; class Debugger;
class DeviceMemory; class DeviceMemory;
class ExclusiveMonitor; class ExclusiveMonitor;
class GPUDirtyMemoryManager;
class PerfStats; class PerfStats;
class Reporter; class Reporter;
class SpeedLimiter; class SpeedLimiter;
@ -225,12 +224,6 @@ public:
/// Prepare the core emulation for a reschedule /// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index); void PrepareReschedule(u32 core_index);
/// Provides a reference to the gou dirty memory manager.
[[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
/// Provides a constant reference to the current gou dirty memory manager.
[[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
[[nodiscard]] size_t GetCurrentHostThreadID() const; [[nodiscard]] size_t GetCurrentHostThreadID() const;
@ -250,12 +243,6 @@ public:
/// Gets a const reference to the underlying CPU manager /// Gets a const reference to the underlying CPU manager
[[nodiscard]] const CpuManager& GetCpuManager() const; [[nodiscard]] const CpuManager& GetCpuManager() const;
/// Gets a reference to the exclusive monitor
[[nodiscard]] ExclusiveMonitor& Monitor();
/// Gets a constant reference to the exclusive monitor
[[nodiscard]] const ExclusiveMonitor& Monitor() const;
/// Gets a mutable reference to the system memory instance. /// Gets a mutable reference to the system memory instance.
[[nodiscard]] Core::Memory::Memory& ApplicationMemory(); [[nodiscard]] Core::Memory::Memory& ApplicationMemory();

View File

@ -29,7 +29,6 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
struct CoreTiming::Event { struct CoreTiming::Event {
s64 time; s64 time;
u64 fifo_order; u64 fifo_order;
std::uintptr_t user_data;
std::weak_ptr<EventType> type; std::weak_ptr<EventType> type;
s64 reschedule_time; s64 reschedule_time;
heap_t::handle_type handle{}; heap_t::handle_type handle{};
@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0; event_fifo_id = 0;
shutting_down = false; shutting_down = false;
cpu_ticks = 0; cpu_ticks = 0;
const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) { if (is_multicore) {
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this)); timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
} }
} }
void CoreTiming::ClearPendingEvents() { void CoreTiming::ClearPendingEvents() {
std::scoped_lock lock{basic_lock}; std::scoped_lock lock{advance_lock, basic_lock};
event_queue.clear(); event_queue.clear();
event.Set();
} }
void CoreTiming::Pause(bool is_paused) { void CoreTiming::Pause(bool is_paused) {
@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const {
} }
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type, const std::shared_ptr<EventType>& event_type, bool absolute_time) {
std::uintptr_t user_data, bool absolute_time) {
{ {
std::scoped_lock scope{basic_lock}; std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
auto h{event_queue.emplace( auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
(*h).handle = h; (*h).handle = h;
} }
@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time, std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type, const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) { bool absolute_time) {
{ {
std::scoped_lock scope{basic_lock}; std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, auto h{event_queue.emplace(
resched_time.count()})}; Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
(*h).handle = h; (*h).handle = h;
} }
@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
} }
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool wait) { UnscheduleEventType type) {
{ {
std::scoped_lock lk{basic_lock}; std::scoped_lock lk{basic_lock};
std::vector<heap_t::handle_type> to_remove; std::vector<heap_t::handle_type> to_remove;
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
const Event& e = *itr; const Event& e = *itr;
if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { if (e.type.lock().get() == event_type.get()) {
to_remove.push_back(itr->handle); to_remove.push_back(itr->handle);
} }
} }
@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
for (auto h : to_remove) { for (auto h : to_remove) {
event_queue.erase(h); event_queue.erase(h);
} }
event_type->sequence_number++;
} }
// Force any in-progress events to finish // Force any in-progress events to finish
if (wait) { if (type == UnscheduleEventType::Wait) {
std::scoped_lock lk{advance_lock}; std::scoped_lock lk{advance_lock};
} }
} }
@ -208,28 +205,31 @@ std::optional<s64> CoreTiming::Advance() {
const Event& evt = event_queue.top(); const Event& evt = event_queue.top();
if (const auto event_type{evt.type.lock()}) { if (const auto event_type{evt.type.lock()}) {
if (evt.reschedule_time == 0) { const auto evt_time = evt.time;
const auto evt_user_data = evt.user_data; const auto evt_sequence_num = event_type->sequence_number;
const auto evt_time = evt.time;
if (evt.reschedule_time == 0) {
event_queue.pop(); event_queue.pop();
basic_lock.unlock(); basic_lock.unlock();
event_type->callback( event_type->callback(
evt_user_data, evt_time, evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
basic_lock.lock(); basic_lock.lock();
} else { } else {
basic_lock.unlock(); basic_lock.unlock();
const auto new_schedule_time{event_type->callback( const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time, evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
basic_lock.lock(); basic_lock.lock();
if (evt_sequence_num != event_type->sequence_number) {
// Heap handle is invalidated after external modification.
continue;
}
const auto next_schedule_time{new_schedule_time.has_value() const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count() ? new_schedule_time.value().count()
: evt.reschedule_time}; : evt.reschedule_time};
@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
next_time = pause_end_time + next_schedule_time; next_time = pause_end_time + next_schedule_time;
} }
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
evt.type, next_schedule_time, evt.handle}); next_schedule_time, evt.handle});
} }
} }

View File

@ -22,17 +22,25 @@ namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event. /// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; s64 time, std::chrono::nanoseconds ns_late)>;
/// Contains the characteristics of a particular event. /// Contains the characteristics of a particular event.
struct EventType { struct EventType {
explicit EventType(TimedCallback&& callback_, std::string&& name_) explicit EventType(TimedCallback&& callback_, std::string&& name_)
: callback{std::move(callback_)}, name{std::move(name_)} {} : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
/// The event's callback function. /// The event's callback function.
TimedCallback callback; TimedCallback callback;
/// A pointer to the name of the event. /// A pointer to the name of the event.
const std::string name; const std::string name;
/// A monotonic sequence number, incremented when this event is
/// changed externally.
size_t sequence_number;
};
enum class UnscheduleEventType {
Wait,
NoWait,
}; };
/** /**
@ -89,23 +97,17 @@ public:
/// Schedules an event in core timing /// Schedules an event in core timing
void ScheduleEvent(std::chrono::nanoseconds ns_into_future, void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
bool absolute_time = false);
/// Schedules an event which will automatically re-schedule itself with the given time, until /// Schedules an event which will automatically re-schedule itself with the given time, until
/// unscheduled /// unscheduled
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time, std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type, const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data = 0, bool absolute_time = false); bool absolute_time = false);
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data, void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
bool wait = true); UnscheduleEventType type = UnscheduleEventType::Wait);
void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data) {
UnscheduleEvent(event_type, user_data, false);
}
void AddTicks(u64 ticks_to_add); void AddTicks(u64 ticks_to_add);
@ -158,7 +160,6 @@ private:
heap_t event_queue; heap_t event_queue;
u64 event_fifo_id = 0; u64 event_fifo_id = 0;
std::shared_ptr<EventType> ev_lost;
Common::Event event{}; Common::Event event{};
Common::Event pause_event{}; Common::Event pause_event{};
mutable std::mutex basic_lock; mutable std::mutex basic_lock;

View File

@ -73,6 +73,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
return nullptr; return nullptr;
auto in_data = in->ReadAllBytes(); auto in_data = in->ReadAllBytes();
if (in_data.size() == 0) {
return nullptr;
}
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header u64 offset = 5; // After header
@ -88,6 +91,10 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
else else
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
if (real_offset > in_data.size()) {
return nullptr;
}
u16 data_size{}; u16 data_size{};
if (ips->ReadObject(&data_size, offset) != sizeof(u16)) if (ips->ReadObject(&data_size, offset) != sizeof(u16))
return nullptr; return nullptr;

View File

@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
return npdm_header.system_resource_size; return npdm_header.system_resource_size;
} }
PoolPartition ProgramMetadata::GetPoolPartition() const {
return acid_header.pool_partition;
}
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
return aci_kernel_capabilities; return aci_kernel_capabilities;
} }
@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
// Begin ACID printing (potential perms, signed) // Begin ACID printing (potential perms, signed)
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);

View File

@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
Everything = 1ULL << 63, Everything = 1ULL << 63,
}; };
enum class PoolPartition : u32 {
Application = 0,
Applet = 1,
System = 2,
SystemNonSecure = 3,
};
/** /**
* Helper which implements an interface to parse Program Description Metadata (NPDM) * Helper which implements an interface to parse Program Description Metadata (NPDM)
* Data can either be loaded from a file path or with data and an offset into it. * Data can either be loaded from a file path or with data and an offset into it.
@ -72,6 +79,7 @@ public:
u64 GetTitleID() const; u64 GetTitleID() const;
u64 GetFilesystemPermissions() const; u64 GetFilesystemPermissions() const;
u32 GetSystemResourceSize() const; u32 GetSystemResourceSize() const;
PoolPartition GetPoolPartition() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const; const KernelCapabilityDescriptors& GetKernelCapabilities() const;
const std::array<u8, 0x10>& GetName() const { const std::array<u8, 0x10>& GetName() const {
return npdm_header.application_name; return npdm_header.application_name;
@ -116,8 +124,9 @@ private:
union { union {
u32 flags; u32 flags;
BitField<0, 1, u32> is_retail; BitField<0, 1, u32> production_flag;
BitField<1, 31, u32> flags_unk; BitField<1, 1, u32> unqualified_approval;
BitField<2, 4, PoolPartition> pool_partition;
}; };
u64_le title_id_min; u64_le title_id_min;
u64_le title_id_max; u64_le title_id_max;

View File

@ -4,6 +4,7 @@
#include "core/arm/exclusive_monitor.h" #include "core/arm/exclusive_monitor.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
@ -26,9 +27,9 @@ bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
return true; return true;
} }
bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) { bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
auto& monitor = system.Monitor(); auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); const auto current_core = kernel.CurrentPhysicalCoreIndex();
// NOTE: If scheduler lock is not held here, interrupt disable is required. // NOTE: If scheduler lock is not held here, interrupt disable is required.
// KScopedInterruptDisable di; // KScopedInterruptDisable di;
@ -66,10 +67,10 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
return true; return true;
} }
bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value, bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
s32 new_value) { s32 new_value) {
auto& monitor = system.Monitor(); auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); const auto current_core = kernel.CurrentPhysicalCoreIndex();
// NOTE: If scheduler lock is not held here, interrupt disable is required. // NOTE: If scheduler lock is not held here, interrupt disable is required.
// KScopedInterruptDisable di; // KScopedInterruptDisable di;
@ -159,7 +160,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
// Check the userspace value. // Check the userspace value.
s32 user_value{}; s32 user_value{};
R_UNLESS(UpdateIfEqual(m_system, std::addressof(user_value), addr, value, value + 1), R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
ResultInvalidCurrentMemory); ResultInvalidCurrentMemory);
R_UNLESS(user_value == value, ResultInvalidState); R_UNLESS(user_value == value, ResultInvalidState);
@ -219,7 +220,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
s32 user_value{}; s32 user_value{};
bool succeeded{}; bool succeeded{};
if (value != new_value) { if (value != new_value) {
succeeded = UpdateIfEqual(m_system, std::addressof(user_value), addr, value, new_value); succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
} else { } else {
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
} }
@ -262,7 +263,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
s32 user_value{}; s32 user_value{};
bool succeeded{}; bool succeeded{};
if (decrement) { if (decrement) {
succeeded = DecrementIfLessThan(m_system, std::addressof(user_value), addr, value); succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
} else { } else {
succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
} }

View File

@ -8,19 +8,22 @@
namespace Kernel { namespace Kernel {
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock); // KScopedInterruptDisable di;
KScopedSpinLock lk(m_lock);
m_object_list.insert_unique(*obj); m_object_list.insert_unique(*obj);
} }
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
KScopedLightLock lk(m_lock); // KScopedInterruptDisable di;
KScopedSpinLock lk(m_lock);
m_object_list.erase(*obj); m_object_list.erase(*obj);
} }
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
KScopedLightLock lk(m_lock); // KScopedInterruptDisable di;
KScopedSpinLock lk(m_lock);
return std::count_if(m_object_list.begin(), m_object_list.end(), return std::count_if(m_object_list.begin(), m_object_list.end(),
[&](const auto& obj) { return obj.GetOwner() == owner; }); [&](const auto& obj) { return obj.GetOwner() == owner; });

View File

@ -7,7 +7,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_spin_lock.h"
namespace Kernel { namespace Kernel {
@ -21,32 +21,7 @@ public:
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
class ListAccessor : public KScopedLightLock { KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
public:
explicit ListAccessor(KAutoObjectWithListContainer* container)
: KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
explicit ListAccessor(KAutoObjectWithListContainer& container)
: KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
typename ListType::iterator begin() const {
return m_list.begin();
}
typename ListType::iterator end() const {
return m_list.end();
}
typename ListType::iterator find(typename ListType::const_reference ref) const {
return m_list.find(ref);
}
private:
ListType& m_list;
};
friend class ListAccessor;
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
void Initialize() {} void Initialize() {}
void Finalize() {} void Finalize() {}
@ -56,7 +31,7 @@ public:
size_t GetOwnedCount(KProcess* owner); size_t GetOwnedCount(KProcess* owner);
private: private:
KLightLock m_lock; KSpinLock m_lock;
ListType m_object_list; ListType m_object_list;
}; };

View File

@ -185,6 +185,10 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
case RegionType::NoMapping: case RegionType::NoMapping:
break; break;
case RegionType::KernelTraceBuffer: case RegionType::KernelTraceBuffer:
if constexpr (!IsKTraceEnabled) {
break;
}
[[fallthrough]];
case RegionType::OnMemoryBootImage: case RegionType::OnMemoryBootImage:
case RegionType::DTB: case RegionType::DTB:
R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm)); R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
@ -330,8 +334,6 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTab
// Map the range. // Map the range.
R_TRY(this->MapRange_(cap, size_cap, page_table)); R_TRY(this->MapRange_(cap, size_cap, page_table));
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
continue;
} else { } else {
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
} }

View File

@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
KSession* session{}; KSession* session{};
// Reserve a new session from the resource limit. // Reserve a new session from the resource limit.
//! FIXME: we are reserving this from the wrong resource limit! KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
KScopedResourceReservation session_reservation( LimitableResource::SessionCountMax);
m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Allocate a session normally. // Allocate a session normally.

View File

@ -28,10 +28,10 @@ bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
return true; return true;
} }
bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero, bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 if_zero,
u32 new_orr_mask) { u32 new_orr_mask) {
auto& monitor = system.Monitor(); auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); const auto current_core = kernel.CurrentPhysicalCoreIndex();
u32 expected{}; u32 expected{};
@ -208,7 +208,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should call CanAccessAtomic(..) here. // TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true; can_access = true;
if (can_access) [[likely]] { if (can_access) [[likely]] {
UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag, UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask); Svc::HandleWaitMask);
} }
} }

View File

@ -90,8 +90,7 @@ public:
// Handle pseudo-handles. // Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) { if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) { if (handle == Svc::PseudoHandle::CurrentProcess) {
//! FIXME: this is the wrong process! auto* const cur_process = GetCurrentProcessPointer(m_kernel);
auto* const cur_process = m_kernel.ApplicationProcess();
ASSERT(cur_process != nullptr); ASSERT(cur_process != nullptr);
return cur_process; return cur_process;
} }

View File

@ -10,15 +10,15 @@ namespace Kernel {
void KHardwareTimer::Initialize() { void KHardwareTimer::Initialize() {
// Create the timing callback to register with CoreTiming. // Create the timing callback to register with CoreTiming.
m_event_type = Core::Timing::CreateEvent( m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
"KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { [this](s64, std::chrono::nanoseconds) {
reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); this->DoTask();
return std::nullopt; return std::nullopt;
}); });
} }
void KHardwareTimer::Finalize() { void KHardwareTimer::Finalize() {
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
m_wakeup_time = std::numeric_limits<s64>::max(); m_wakeup_time = std::numeric_limits<s64>::max();
m_event_type.reset(); m_event_type.reset();
} }
@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
m_wakeup_time = wakeup_time; m_wakeup_time = wakeup_time;
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
m_event_type, reinterpret_cast<uintptr_t>(this), m_event_type, true);
true);
} }
void KHardwareTimer::DisableInterrupt() { void KHardwareTimer::DisableInterrupt() {
m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type, m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
reinterpret_cast<uintptr_t>(this)); Core::Timing::UnscheduleEventType::NoWait);
m_wakeup_time = std::numeric_limits<s64>::max(); m_wakeup_time = std::numeric_limits<s64>::max();
} }

View File

@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
void KPageTableBase::Finalize() { void KPageTableBase::Finalize() {
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
if (Settings::IsFastmemEnabled()) { if (Settings::IsFastmemEnabled()) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
} }
}; };
@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
// Unmap. // Unmap.
R_ASSERT(this->Operate(updater.GetPageList(), cur_address, R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
cur_pages, 0, false, unmap_properties, cur_pages, 0, false, unmap_properties,
OperationType::Unmap, true)); OperationType::UnmapPhysical, true));
} }
// Check if we're done. // Check if we're done.
@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
// Map the papges. // Map the papges.
R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
cur_pg, map_properties, cur_pg, map_properties,
OperationType::MapFirstGroup, false)); OperationType::MapFirstGroupPhysical, false));
} }
} }
@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
// Unmap. // Unmap.
R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
unmap_properties, OperationType::Unmap, false)); unmap_properties, OperationType::UnmapPhysical, false));
} }
// Check if we're done. // Check if we're done.
@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
// or free them to the page list, and so it goes unused (along with page properties). // or free them to the page list, and so it goes unused (along with page properties).
switch (operation) { switch (operation) {
case OperationType::Unmap: { case OperationType::Unmap:
case OperationType::UnmapPhysical: {
const bool separate_heap = operation == OperationType::UnmapPhysical;
// Ensure that any pages we track are closed on exit. // Ensure that any pages we track are closed on exit.
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
this->MakePageGroup(pages_to_close, virt_addr, num_pages); this->MakePageGroup(pages_to_close, virt_addr, num_pages);
// Unmap. // Unmap.
m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
R_SUCCEED(); R_SUCCEED();
} }
@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
ASSERT(virt_addr != 0); ASSERT(virt_addr != 0);
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
ConvertToMemoryPermission(properties.perm)); ConvertToMemoryPermission(properties.perm), false);
// Open references to pages, if we should. // Open references to pages, if we should.
if (this->IsHeapPhysicalAddress(phys_addr)) { if (this->IsHeapPhysicalAddress(phys_addr)) {
@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
switch (operation) { switch (operation) {
case OperationType::MapGroup: case OperationType::MapGroup:
case OperationType::MapFirstGroup: { case OperationType::MapFirstGroup:
case OperationType::MapFirstGroupPhysical: {
const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
// We want to maintain a new reference to every page in the group. // We want to maintain a new reference to every page in the group.
KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
for (const auto& node : page_group) { for (const auto& node : page_group) {
const size_t size{node.GetNumPages() * PageSize}; const size_t size{node.GetNumPages() * PageSize};
// Map the pages. // Map the pages.
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
ConvertToMemoryPermission(properties.perm)); ConvertToMemoryPermission(properties.perm), separate_heap);
virt_addr += size; virt_addr += size;
} }

View File

@ -104,6 +104,9 @@ protected:
ChangePermissionsAndRefresh = 5, ChangePermissionsAndRefresh = 5,
ChangePermissionsAndRefreshAndFlush = 6, ChangePermissionsAndRefreshAndFlush = 6,
Separate = 7, Separate = 7,
MapFirstGroupPhysical = 65000,
UnmapPhysical = 65001,
}; };
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;

View File

@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
params.code_address, params.code_num_pages * PageSize, params.code_address, params.code_num_pages * PageSize,
m_system_resource, res_limit, this->GetMemory(), 0)); m_system_resource, res_limit, m_memory, 0));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
}; };
// Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
// Ensure we can insert the code region. // Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
KMemoryState::Code), KMemoryState::Code),
@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
params.code_address, code_size, m_system_resource, res_limit, params.code_address, code_size, m_system_resource, res_limit,
this->GetMemory(), aslr_space_start)); m_memory, aslr_space_start));
} }
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
m_page_table.Finalize(); m_page_table.Finalize();
}; };
// Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
// Ensure we can insert the code region. // Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
ResultInvalidMemoryRegion); ResultInvalidMemoryRegion);
@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
s32 max_out_count) { s32 max_out_count) {
// TODO: use current memory reference auto& memory = this->GetMemory();
auto& memory = m_kernel.System().ApplicationMemory();
// Lock the list. // Lock the list.
KScopedLightLock lk(m_list_lock); KScopedLightLock lk(m_list_lock);
@ -1128,14 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel) KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
m_handle_table{kernel} {} m_handle_table{kernel}, m_dirty_memory_managers{},
m_exclusive_monitor{}, m_memory{kernel.System()} {}
KProcess::~KProcess() = default; KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, bool is_hbl) { KProcessAddress aslr_space_start, bool is_hbl) {
// Create a resource limit for the process. // Create a resource limit for the process.
const auto physical_memory_size = const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
auto* res_limit = auto* res_limit =
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
@ -1146,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
Svc::CreateProcessFlag flag{}; Svc::CreateProcessFlag flag{};
u64 code_address{}; u64 code_address{};
// We are an application. // Determine if we are an application.
flag |= Svc::CreateProcessFlag::IsApplication; if (pool == KMemoryManager::Pool::Application) {
flag |= Svc::CreateProcessFlag::IsApplication;
}
// If we are 64-bit, create as such. // If we are 64-bit, create as such.
if (metadata.Is64BitProgram()) { if (metadata.Is64BitProgram()) {
@ -1196,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
std::memcpy(params.name.data(), name.data(), sizeof(params.name)); std::memcpy(params.name.data(), name.data(), sizeof(params.name));
// Initialize for application process. // Initialize for application process.
R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
KMemoryManager::Pool::Application, aslr_space_start)); aslr_space_start));
// Assign remaining properties. // Assign remaining properties.
m_is_hbl = is_hbl; m_is_hbl = is_hbl;
@ -1223,22 +1233,25 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef HAS_NCE #ifdef HAS_NCE
if (Settings::IsNceEnabled()) { if (this->IsApplication() && Settings::IsNceEnabled()) {
auto& buffer = m_kernel.System().DeviceMemory().buffer; auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment(); const auto& code = code_set.CodeSegment();
const auto& patch = code_set.PatchSegment(); const auto& patch = code_set.PatchSegment();
buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); buffer.Protect(GetInteger(base_addr + code.addr), code.size,
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
} }
#endif #endif
} }
void KProcess::InitializeInterfaces() { void KProcess::InitializeInterfaces() {
this->GetMemory().SetCurrentPageTable(*this); m_exclusive_monitor =
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
#ifdef HAS_NCE #ifdef HAS_NCE
if (this->Is64Bit() && Settings::IsNceEnabled()) { if (this->IsApplication() && Settings::IsNceEnabled()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
} }
@ -1248,13 +1261,13 @@ void KProcess::InitializeInterfaces() {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>( m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
m_kernel.System(), m_kernel.IsMulticore(), this, m_kernel.System(), m_kernel.IsMulticore(), this,
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
} }
} else { } else {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>( m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
m_kernel.System(), m_kernel.IsMulticore(), this, m_kernel.System(), m_kernel.IsMulticore(), this,
static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
} }
} }
} }
@ -1305,9 +1318,10 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
return true; return true;
} }
Core::Memory::Memory& KProcess::GetMemory() const { void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
// TODO: per-process memory for (auto& manager : m_dirty_memory_managers) {
return m_kernel.System().ApplicationMemory(); manager.Gather(callback);
}
} }
} // namespace Kernel } // namespace Kernel

View File

@ -7,6 +7,7 @@
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h" #include "core/file_sys/program_metadata.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
@ -17,6 +18,7 @@
#include "core/hle/kernel/k_system_resource.h" #include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h" #include "core/hle/kernel/k_thread_local_page.h"
#include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -126,6 +128,9 @@ private:
#ifdef HAS_NCE #ifdef HAS_NCE
std::unordered_map<u64, u64> m_post_handlers{}; std::unordered_map<u64, u64> m_post_handlers{};
#endif #endif
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
Core::Memory::Memory m_memory;
private: private:
Result StartTermination(); Result StartTermination();
@ -502,7 +507,15 @@ public:
void InitializeInterfaces(); void InitializeInterfaces();
Core::Memory::Memory& GetMemory() const; Core::Memory::Memory& GetMemory() {
return m_memory;
}
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
Core::ExclusiveMonitor& GetExclusiveMonitor() const {
return *m_exclusive_monitor;
}
public: public:
// Overridden parent functions. // Overridden parent functions.

File diff suppressed because it is too large Load Diff

View File

@ -49,14 +49,21 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void OnClientClosed(); void OnClientClosed();
/// TODO: flesh these out to match the real kernel
Result OnRequest(KSessionRequest* request); Result OnRequest(KSessionRequest* request);
Result SendReply(bool is_hle = false); Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
Result ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context = nullptr, KPhysicalAddress server_message_paddr, bool is_hle = false);
Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
KPhysicalAddress server_message_paddr,
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
std::weak_ptr<Service::SessionRequestManager> manager = {}); std::weak_ptr<Service::SessionRequestManager> manager = {});
Result SendReplyHLE() { Result SendReplyHLE() {
return SendReply(true); R_RETURN(this->SendReply(0, 0, 0, true));
}
Result ReceiveRequestHLE(std::shared_ptr<Service::HLERequestContext>* out_context,
std::weak_ptr<Service::SessionRequestManager> manager) {
R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager));
} }
private: private:

View File

@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
m_name = name; m_name = name;
// Set our owner process. // Set our owner process.
//! FIXME: this is the wrong process! m_process = GetCurrentProcessPointer(m_kernel);
m_process = m_kernel.ApplicationProcess();
m_process->Open(); m_process->Open();
// Set our port. // Set our port.

View File

@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
} }
Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
// TODO: per-process memory return GetCurrentProcess(kernel).GetMemory();
return kernel.System().ApplicationMemory();
} }
KScopedDisableDispatch::~KScopedDisableDispatch() { KScopedDisableDispatch::~KScopedDisableDispatch() {

View File

@ -314,11 +314,7 @@ public:
m_current_core_id = core; m_current_core_id = core;
} }
KProcess* GetOwnerProcess() { KProcess* GetOwnerProcess() const {
return m_parent;
}
const KProcess* GetOwnerProcess() const {
return m_parent; return m_parent;
} }

View File

@ -5,6 +5,7 @@
#include <optional> #include <optional>
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h" #include "core/hle/kernel/svc_types.h"

View File

@ -68,8 +68,6 @@ struct KernelCore::Impl {
global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
is_phantom_mode_for_singlecore = false; is_phantom_mode_for_singlecore = false;
@ -121,13 +119,8 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIdMin; next_user_process_id = KProcess::ProcessIdMin;
next_thread_id = 1; next_thread_id = 1;
global_handle_table->Finalize();
global_handle_table.reset();
preemption_event = nullptr; preemption_event = nullptr;
exclusive_monitor.reset();
// Cleanup persistent kernel objects // Cleanup persistent kernel objects
auto CleanupObject = [](KAutoObject* obj) { auto CleanupObject = [](KAutoObject* obj) {
if (obj) { if (obj) {
@ -191,8 +184,6 @@ struct KernelCore::Impl {
} }
void InitializePhysicalCores() { void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.ApplicationMemory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
const s32 core{static_cast<s32>(i)}; const s32 core{static_cast<s32>(i)};
@ -247,7 +238,7 @@ struct KernelCore::Impl {
void InitializePreemption(KernelCore& kernel) { void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent( preemption_event = Core::Timing::CreateEvent(
"PreemptionCallback", "PreemptionCallback",
[this, &kernel](std::uintptr_t, s64 time, [this, &kernel](s64 time,
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
{ {
KScopedSchedulerLock lock(kernel); KScopedSchedulerLock lock(kernel);
@ -791,10 +782,6 @@ struct KernelCore::Impl {
std::shared_ptr<Core::Timing::EventType> preemption_event; std::shared_ptr<Core::Timing::EventType> preemption_event;
// This is the kernel's handle table or supervisor handle table which
// stores all the objects in place.
std::unique_ptr<KHandleTable> global_handle_table;
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
std::unique_ptr<KObjectNameGlobalData> object_name_global_data; std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
@ -805,7 +792,6 @@ struct KernelCore::Impl {
std::mutex server_lock; std::mutex server_lock;
std::vector<std::unique_ptr<Service::ServerManager>> server_managers; std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
@ -882,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
return impl->system_resource_limit; return impl->system_resource_limit;
} }
KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table->GetObject<KThread>(handle);
}
void KernelCore::AppendNewProcess(KProcess* process) { void KernelCore::AppendNewProcess(KProcess* process) {
impl->process_list.push_back(process); impl->process_list.push_back(process);
} }
@ -959,14 +941,6 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer; return *impl->hardware_timer;
} }
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
return *impl->exclusive_monitor;
}
const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
return *impl->exclusive_monitor;
}
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
return *impl->global_object_list_container; return *impl->global_object_list_container;
} }
@ -1030,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
return impl->next_user_process_id++; return impl->next_user_process_id++;
} }
KHandleTable& KernelCore::GlobalHandleTable() {
return *impl->global_handle_table;
}
const KHandleTable& KernelCore::GlobalHandleTable() const {
return *impl->global_handle_table;
}
void KernelCore::RegisterCoreThread(std::size_t core_id) { void KernelCore::RegisterCoreThread(std::size_t core_id) {
impl->RegisterCoreThread(core_id); impl->RegisterCoreThread(core_id);
} }

View File

@ -116,9 +116,6 @@ public:
/// Retrieves a shared pointer to the system resource limit instance. /// Retrieves a shared pointer to the system resource limit instance.
KResourceLimit* GetSystemResourceLimit(); KResourceLimit* GetSystemResourceLimit();
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes. /// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(KProcess* process); void AppendNewProcess(KProcess* process);
@ -170,10 +167,6 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread. /// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id); void PrepareReschedule(std::size_t id);
Core::ExclusiveMonitor& GetExclusiveMonitor();
const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
KAutoObjectWithListContainer& ObjectListContainer(); KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer& ObjectListContainer() const; const KAutoObjectWithListContainer& ObjectListContainer() const;

View File

@ -18,13 +18,13 @@ public:
static constexpr inline u64 NullTag = 0; static constexpr inline u64 NullTag = 0;
public: public:
enum class ReceiveListCountType : u32 { enum ReceiveListCountType : u32 {
None = 0, ReceiveListCountType_None = 0,
ToMessageBuffer = 1, ReceiveListCountType_ToMessageBuffer = 1,
ToSingleBuffer = 2, ReceiveListCountType_ToSingleBuffer = 2,
CountOffset = 2, ReceiveListCountType_CountOffset = 2,
CountMax = 13, ReceiveListCountType_CountMax = 13,
}; };
private: private:
@ -591,16 +591,16 @@ public:
// Add the size of the receive list. // Add the size of the receive list.
const auto count = hdr.GetReceiveListCount(); const auto count = hdr.GetReceiveListCount();
switch (count) { switch (count) {
case MessageHeader::ReceiveListCountType::None: case MessageHeader::ReceiveListCountType_None:
break; break;
case MessageHeader::ReceiveListCountType::ToMessageBuffer: case MessageHeader::ReceiveListCountType_ToMessageBuffer:
break; break;
case MessageHeader::ReceiveListCountType::ToSingleBuffer: case MessageHeader::ReceiveListCountType_ToSingleBuffer:
msg_size += ReceiveListEntry::GetDataSize(); msg_size += ReceiveListEntry::GetDataSize();
break; break;
default: default:
msg_size += (static_cast<s32>(count) - msg_size += (static_cast<s32>(count) -
static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) * static_cast<s32>(MessageHeader::ReceiveListCountType_CountOffset)) *
ReceiveListEntry::GetDataSize(); ReceiveListEntry::GetDataSize();
break; break;
} }

View File

@ -118,7 +118,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED(); R_SUCCEED();
case InfoType::IsApplication: case InfoType::IsApplication:
LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
*result = process->IsApplication(); *result = process->IsApplication();
R_SUCCEED(); R_SUCCEED();

View File

@ -48,8 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
}; };
// Send the reply. // Send the reply.
R_TRY(session->SendReply()); R_TRY(session->SendReply(message, buffer_size, message_paddr));
// R_TRY(session->SendReply(message, buffer_size, message_paddr));
} }
// Receive a message. // Receive a message.
@ -85,8 +84,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
if (R_SUCCEEDED(result)) { if (R_SUCCEEDED(result)) {
KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
if (session != nullptr) { if (session != nullptr) {
// result = session->ReceiveRequest(message, buffer_size, message_paddr); result = session->ReceiveRequest(message, buffer_size, message_paddr);
result = session->ReceiveRequest();
if (ResultNotFound == result) { if (ResultNotFound == result) {
continue; continue;
} }

View File

@ -38,7 +38,9 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
} // namespace Kernel } // namespace Kernel

View File

@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
return; return;
} }
auto transfer_mem = auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) { if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
} }
std::vector<u8> memory(transfer_mem->GetSize()); std::vector<u8> memory(transfer_mem->GetSize());
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
memory.size());
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
return; return;
} }
auto transfer_mem = auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) { if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
} }
std::vector<u8> memory(transfer_mem->GetSize()); std::vector<u8> memory(transfer_mem->GetSize());
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
memory.size());
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
return; return;
} }
const auto& handle_table{system.ApplicationProcess()->GetHandleTable()}; auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
auto transfer_memory{
process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
const auto session_id{impl->GetSessionId()}; const auto session_id{impl->GetSessionId()};
if (session_id == -1) { if (session_id == -1) {

View File

@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
auto params = rp.PopRaw<OpusParameters>(); auto params = rp.PopRaw<OpusParameters>();
auto transfer_memory_size{rp.Pop<u32>()}; auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)}; auto transfer_memory_handle{ctx.GetCopyHandle(0)};
auto transfer_memory{ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
transfer_memory_handle)};
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
params.sample_rate, params.channel_count, transfer_memory_size); params.sample_rate, params.channel_count, transfer_memory_size);
@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
auto transfer_memory_size{rp.Pop<u32>()}; auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)}; auto transfer_memory_handle{ctx.GetCopyHandle(0)};
auto transfer_memory{ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
transfer_memory_handle)};
LOG_DEBUG(Service_Audio, LOG_DEBUG(Service_Audio,
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
auto params = rp.PopRaw<OpusParametersEx>(); auto params = rp.PopRaw<OpusParametersEx>();
auto transfer_memory_size{rp.Pop<u32>()}; auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)}; auto transfer_memory_handle{ctx.GetCopyHandle(0)};
auto transfer_memory{ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
transfer_memory_handle)};
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
params.sample_rate, params.channel_count, transfer_memory_size); params.sample_rate, params.channel_count, transfer_memory_size);
@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
auto transfer_memory_size{rp.Pop<u32>()}; auto transfer_memory_size{rp.Pop<u32>()};
auto transfer_memory_handle{ctx.GetCopyHandle(0)}; auto transfer_memory_handle{ctx.GetCopyHandle(0)};
auto transfer_memory{ auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
transfer_memory_handle)};
LOG_DEBUG(Service_Audio, LOG_DEBUG(Service_Audio,
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "

View File

@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
crash_report += fmt::format(" ESR: {:016x}\n", info.esr); crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
crash_report += fmt::format(" FAR: {:016x}\n", info.far); crash_report += fmt::format(" FAR: {:016x}\n", info.far);
crash_report += "\nBacktrace:\n"; crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) { for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
crash_report += crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
} }

View File

@ -1850,8 +1850,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
t_mem_1_handle);
if (t_mem_1.IsNull()) { if (t_mem_1.IsNull()) {
LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
@ -1860,8 +1859,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
return; return;
} }
auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
t_mem_2_handle);
if (t_mem_2.IsNull()) { if (t_mem_2.IsNull()) {
LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
@ -2142,8 +2140,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
t_mem_handle);
if (t_mem.IsNull()) { if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);

View File

@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
// Register update callbacks // Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent( hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback", "Hidbus::UpdateCallback",
[this](std::uintptr_t user_data, s64 time, [this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
UpdateHidbus(user_data, ns_late); UpdateHidbus(ns_late);
return std::nullopt; return std::nullopt;
}); });
@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
} }
HidBus::~HidBus() { HidBus::~HidBus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); system.CoreTiming().UnscheduleEvent(hidbus_update_event);
} }
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) { if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) { for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initializated) { if (!devices[i].is_device_initializated) {
@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
t_mem_handle);
if (t_mem.IsNull()) { if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);

View File

@ -108,7 +108,7 @@ private:
void DisableJoyPollingReceiveMode(HLERequestContext& ctx); void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
void SetStatusManagerType(HLERequestContext& ctx); void SetStatusManagerType(HLERequestContext& ctx);
void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateHidbus(std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
template <typename T> template <typename T>

View File

@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()}; const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)}; const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
t_mem_handle);
if (t_mem.IsNull()) { if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()}; const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)}; const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
t_mem_handle);
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, "

View File

@ -227,8 +227,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
applet_resource->EnableTouchScreen(aruid, is_enabled); applet_resource->EnableTouchScreen(aruid, is_enabled);
} }
void ResourceManager::UpdateControllers(std::uintptr_t user_data, void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming(); auto& core_timing = system.CoreTiming();
debug_pad->OnUpdate(core_timing); debug_pad->OnUpdate(core_timing);
digitizer->OnUpdate(core_timing); digitizer->OnUpdate(core_timing);
@ -241,20 +240,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
capture_button->OnUpdate(core_timing); capture_button->OnUpdate(core_timing);
} }
void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming(); auto& core_timing = system.CoreTiming();
npad->OnUpdate(core_timing); npad->OnUpdate(core_timing);
} }
void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming(); auto& core_timing = system.CoreTiming();
mouse->OnUpdate(core_timing); mouse->OnUpdate(core_timing);
debug_mouse->OnUpdate(core_timing); debug_mouse->OnUpdate(core_timing);
keyboard->OnUpdate(core_timing); keyboard->OnUpdate(core_timing);
} }
void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming(); auto& core_timing = system.CoreTiming();
six_axis->OnUpdate(core_timing); six_axis->OnUpdate(core_timing);
seven_six_axis->OnUpdate(core_timing); seven_six_axis->OnUpdate(core_timing);
@ -273,34 +271,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
// Register update callbacks // Register update callbacks
npad_update_event = Core::Timing::CreateEvent( npad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback", "HID::UpdatePadCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) [this, resource](
-> std::optional<std::chrono::nanoseconds> { s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
resource->UpdateNpad(user_data, ns_late); resource->UpdateNpad(ns_late);
return std::nullopt; return std::nullopt;
}); });
default_update_event = Core::Timing::CreateEvent( default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback", "HID::UpdateDefaultCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) [this, resource](
-> std::optional<std::chrono::nanoseconds> { s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
resource->UpdateControllers(user_data, ns_late); resource->UpdateControllers(ns_late);
return std::nullopt; return std::nullopt;
}); });
mouse_keyboard_update_event = Core::Timing::CreateEvent( mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback", "HID::UpdateMouseKeyboardCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) [this, resource](
-> std::optional<std::chrono::nanoseconds> { s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
resource->UpdateMouseKeyboard(user_data, ns_late); resource->UpdateMouseKeyboard(ns_late);
return std::nullopt; return std::nullopt;
}); });
motion_update_event = Core::Timing::CreateEvent( motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback", "HID::UpdateMotionCallback",
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) [this, resource](
-> std::optional<std::chrono::nanoseconds> { s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
resource->UpdateMotion(user_data, ns_late); resource->UpdateMotion(ns_late);
return std::nullopt; return std::nullopt;
}); });
@ -314,10 +312,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
} }
IAppletResource::~IAppletResource() { IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(npad_update_event, 0); system.CoreTiming().UnscheduleEvent(npad_update_event);
system.CoreTiming().UnscheduleEvent(default_update_event, 0); system.CoreTiming().UnscheduleEvent(default_update_event);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0); system.CoreTiming().UnscheduleEvent(motion_update_event);
resource_manager->FreeAppletResourceId(aruid); resource_manager->FreeAppletResourceId(aruid);
} }

Some files were not shown because too many files have changed in this diff Show More