summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt22
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt17
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt145
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt109
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt35
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_setup.xml60
-rw-r--r--src/android/app/src/main/res/layout-w600dp/page_setup.xml69
-rw-r--r--src/android/app/src/main/res/layout/dialog_slider.xml13
-rw-r--r--src/android/app/src/main/res/layout/fragment_setup.xml62
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml62
-rw-r--r--src/android/app/src/main/res/layout/page_setup.xml30
-rw-r--r--src/android/app/src/main/res/values/strings.xml2
-rw-r--r--src/core/debugger/gdbstub.cpp17
-rw-r--r--src/core/hid/hid_types.h26
-rw-r--r--src/core/hle/kernel/k_hardware_timer.h9
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp11
-rw-r--r--src/core/hle/kernel/k_resource_limit.h3
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_address_arbiter.cpp3
-rw-r--r--src/core/hle/kernel/svc/svc_condition_variable.cpp3
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp20
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp16
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp29
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h16
-rw-r--r--src/core/hle/service/hid/hid.cpp68
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp1
-rw-r--r--src/core/hle/service/nvnflinger/window.h1
-rw-r--r--src/core/hle/service/sockets/sockets.h2
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp4
-rw-r--r--src/core/hle/service/ssl/ssl_backend_securetransport.cpp2
-rw-r--r--src/core/internal_network/network.cpp43
-rw-r--r--src/core/internal_network/network.h2
-rw-r--r--src/core/internal_network/network_interface.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp187
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h9
51 files changed, 827 insertions, 403 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2da983cad..7bb88c8ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -134,7 +134,7 @@ else()
134 endif() 134 endif()
135 135
136 # GCC bugs 136 # GCC bugs
137 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 137 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
138 # These diagnostics would be great if they worked, but are just completely broken 138 # These diagnostics would be great if they worked, but are just completely broken
139 # and produce bogus errors on external libraries like fmt. 139 # and produce bogus errors on external libraries like fmt.
140 add_compile_options( 140 add_compile_options(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index 7006651d0..bc6ff1364 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -49,6 +49,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List
49 val context = YuzuApplication.appContext 49 val context = YuzuApplication.appContext
50 binding.textSettingName.text = context.getString(license.titleId) 50 binding.textSettingName.text = context.getString(license.titleId)
51 binding.textSettingDescription.text = context.getString(license.descriptionId) 51 binding.textSettingDescription.text = context.getString(license.descriptionId)
52 binding.textSettingValue.visibility = View.GONE
52 } 53 }
53 } 54 }
54} 55}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 481ddd5a5..6b46d359e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,13 +5,19 @@ package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.Html 6import android.text.Html
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View
8import android.view.ViewGroup 9import android.view.ViewGroup
9import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
10import androidx.core.content.res.ResourcesCompat 11import androidx.core.content.res.ResourcesCompat
12import androidx.lifecycle.ViewModelProvider
11import androidx.recyclerview.widget.RecyclerView 13import androidx.recyclerview.widget.RecyclerView
12import com.google.android.material.button.MaterialButton 14import com.google.android.material.button.MaterialButton
13import org.yuzu.yuzu_emu.databinding.PageSetupBinding 15import org.yuzu.yuzu_emu.databinding.PageSetupBinding
16import org.yuzu.yuzu_emu.model.HomeViewModel
17import org.yuzu.yuzu_emu.model.SetupCallback
14import org.yuzu.yuzu_emu.model.SetupPage 18import org.yuzu.yuzu_emu.model.SetupPage
19import org.yuzu.yuzu_emu.model.StepState
20import org.yuzu.yuzu_emu.utils.ViewUtils
15 21
16class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : 22class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
17 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { 23 RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@@ -26,7 +32,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
26 holder.bind(pages[position]) 32 holder.bind(pages[position])
27 33
28 inner class SetupPageViewHolder(val binding: PageSetupBinding) : 34 inner class SetupPageViewHolder(val binding: PageSetupBinding) :
29 RecyclerView.ViewHolder(binding.root) { 35 RecyclerView.ViewHolder(binding.root), SetupCallback {
30 lateinit var page: SetupPage 36 lateinit var page: SetupPage
31 37
32 init { 38 init {
@@ -35,6 +41,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
35 41
36 fun bind(page: SetupPage) { 42 fun bind(page: SetupPage) {
37 this.page = page 43 this.page = page
44
45 if (page.stepCompleted.invoke() == StepState.COMPLETE) {
46 binding.buttonAction.visibility = View.INVISIBLE
47 binding.textConfirmation.visibility = View.VISIBLE
48 }
49
38 binding.icon.setImageDrawable( 50 binding.icon.setImageDrawable(
39 ResourcesCompat.getDrawable( 51 ResourcesCompat.getDrawable(
40 activity.resources, 52 activity.resources,
@@ -62,9 +74,15 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
62 MaterialButton.ICON_GRAVITY_END 74 MaterialButton.ICON_GRAVITY_END
63 } 75 }
64 setOnClickListener { 76 setOnClickListener {
65 page.buttonAction.invoke() 77 page.buttonAction.invoke(this@SetupPageViewHolder)
66 } 78 }
67 } 79 }
68 } 80 }
81
82 override fun onStepCompleted() {
83 ViewUtils.hideView(binding.buttonAction, 200)
84 ViewUtils.showView(binding.textConfirmation, 200)
85 ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
86 }
69 } 87 }
70} 88}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index ce0b92c90..9711e2c51 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -207,8 +207,11 @@ class SettingsAdapter(
207 val sliderBinding = DialogSliderBinding.inflate(inflater) 207 val sliderBinding = DialogSliderBinding.inflate(inflater)
208 208
209 textSliderValue = sliderBinding.textValue 209 textSliderValue = sliderBinding.textValue
210 textSliderValue!!.text = sliderProgress.toString() 210 textSliderValue!!.text = String.format(
211 sliderBinding.textUnits.text = item.units 211 context.getString(R.string.value_with_units),
212 sliderProgress.toString(),
213 item.units
214 )
212 215
213 sliderBinding.slider.apply { 216 sliderBinding.slider.apply {
214 valueFrom = item.min.toFloat() 217 valueFrom = item.min.toFloat()
@@ -216,7 +219,11 @@ class SettingsAdapter(
216 value = sliderProgress.toFloat() 219 value = sliderProgress.toFloat()
217 addOnChangeListener { _: Slider, value: Float, _: Boolean -> 220 addOnChangeListener { _: Slider, value: Float, _: Boolean ->
218 sliderProgress = value.toInt() 221 sliderProgress = value.toInt()
219 textSliderValue!!.text = sliderProgress.toString() 222 textSliderValue!!.text = String.format(
223 context.getString(R.string.value_with_units),
224 sliderProgress.toString(),
225 item.units
226 )
220 } 227 }
221 } 228 }
222 229
@@ -225,10 +232,6 @@ class SettingsAdapter(
225 .setView(sliderBinding.root) 232 .setView(sliderBinding.root)
226 .setPositiveButton(android.R.string.ok, this) 233 .setPositiveButton(android.R.string.ok, this)
227 .setNegativeButton(android.R.string.cancel, defaultCancelListener) 234 .setNegativeButton(android.R.string.cancel, defaultCancelListener)
228 .setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
229 sliderBinding.slider.value = item.defaultValue!!.toFloat()
230 onClick(dialog, which)
231 }
232 .show() 235 .show()
233 } 236 }
234 237
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 7955532ee..79572fc06 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -25,12 +25,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
25 binding.textSettingDescription.setText(item.descriptionId) 25 binding.textSettingDescription.setText(item.descriptionId)
26 binding.textSettingDescription.visibility = View.VISIBLE 26 binding.textSettingDescription.visibility = View.VISIBLE
27 } else { 27 } else {
28 val epochTime = setting.value.toLong() 28 binding.textSettingDescription.visibility = View.GONE
29 val instant = Instant.ofEpochMilli(epochTime * 1000)
30 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
31 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
32 binding.textSettingDescription.text = dateFormatter.format(zonedTime)
33 } 29 }
30
31 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value.toLong()
33 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
36 binding.textSettingValue.text = dateFormatter.format(zonedTime)
37
38 setStyle(setting.isEditable, binding)
34 } 39 }
35 40
36 override fun onClick(clicked: View) { 41 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 5dad5945f..83a2e94f1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -23,6 +23,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
23 } else { 23 } else {
24 binding.textSettingDescription.visibility = View.GONE 24 binding.textSettingDescription.visibility = View.GONE
25 } 25 }
26 binding.textSettingValue.visibility = View.GONE
27
28 setStyle(setting.isEditable, binding)
26 } 29 }
27 30
28 override fun onClick(clicked: View) { 31 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
index f56460893..0fd1d2eaa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SettingViewHolder.kt
@@ -5,6 +5,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.recyclerview.widget.RecyclerView 7import androidx.recyclerview.widget.RecyclerView
8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 10import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
10 12
@@ -33,4 +35,18 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
33 abstract override fun onClick(clicked: View) 35 abstract override fun onClick(clicked: View)
34 36
35 abstract override fun onLongClick(clicked: View): Boolean 37 abstract override fun onLongClick(clicked: View): Boolean
38
39 fun setStyle(isEditable: Boolean, binding: ListItemSettingBinding) {
40 val opacity = if (isEditable) 1.0f else 0.5f
41 binding.textSettingName.alpha = opacity
42 binding.textSettingDescription.alpha = opacity
43 binding.textSettingValue.alpha = opacity
44 }
45
46 fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) {
47 binding.switchWidget.isEnabled = isEditable
48 val opacity = if (isEditable) 1.0f else 0.5f
49 binding.textSettingName.alpha = opacity
50 binding.textSettingDescription.alpha = opacity
51 }
36} 52}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index e4e321bd3..b42d955aa 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -17,28 +17,33 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
17 override fun bind(item: SettingsItem) { 17 override fun bind(item: SettingsItem) {
18 setting = item 18 setting = item
19 binding.textSettingName.setText(item.nameId) 19 binding.textSettingName.setText(item.nameId)
20 binding.textSettingDescription.visibility = View.VISIBLE
21 if (item.descriptionId != 0) { 20 if (item.descriptionId != 0) {
22 binding.textSettingDescription.setText(item.descriptionId) 21 binding.textSettingDescription.setText(item.descriptionId)
23 } else if (item is SingleChoiceSetting) { 22 binding.textSettingDescription.visibility = View.VISIBLE
24 val resMgr = binding.textSettingDescription.context.resources 23 } else {
24 binding.textSettingDescription.visibility = View.GONE
25 }
26
27 binding.textSettingValue.visibility = View.VISIBLE
28 if (item is SingleChoiceSetting) {
29 val resMgr = binding.textSettingValue.context.resources
25 val values = resMgr.getIntArray(item.valuesId) 30 val values = resMgr.getIntArray(item.valuesId)
26 for (i in values.indices) { 31 for (i in values.indices) {
27 if (values[i] == item.selectedValue) { 32 if (values[i] == item.selectedValue) {
28 binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] 33 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
29 return 34 break
30 } 35 }
31 } 36 }
32 } else if (item is StringSingleChoiceSetting) { 37 } else if (item is StringSingleChoiceSetting) {
33 for (i in item.values!!.indices) { 38 for (i in item.values!!.indices) {
34 if (item.values[i] == item.selectedValue) { 39 if (item.values[i] == item.selectedValue) {
35 binding.textSettingDescription.text = item.choices[i] 40 binding.textSettingValue.text = item.choices[i]
36 return 41 break
37 } 42 }
38 } 43 }
39 } else {
40 binding.textSettingDescription.visibility = View.GONE
41 } 44 }
45
46 setStyle(setting.isEditable, binding)
42 } 47 }
43 48
44 override fun onClick(clicked: View) { 49 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index cc3f39aa5..a23b5d109 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import org.yuzu.yuzu_emu.R
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
@@ -22,6 +23,14 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
22 } else { 23 } else {
23 binding.textSettingDescription.visibility = View.GONE 24 binding.textSettingDescription.visibility = View.GONE
24 } 25 }
26 binding.textSettingValue.visibility = View.VISIBLE
27 binding.textSettingValue.text = String.format(
28 binding.textSettingValue.context.getString(R.string.value_with_units),
29 setting.selectedValue,
30 setting.units
31 )
32
33 setStyle(setting.isEditable, binding)
25 } 34 }
26 35
27 override fun onClick(clicked: View) { 36 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index c545b4174..1cf581a9d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -22,6 +22,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
22 } else { 22 } else {
23 binding.textSettingDescription.visibility = View.GONE 23 binding.textSettingDescription.visibility = View.GONE
24 } 24 }
25 binding.textSettingValue.visibility = View.GONE
25 } 26 }
26 27
27 override fun onClick(clicked: View) { 28 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index 54f531795..ef34bf5f4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -25,12 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
25 binding.textSettingDescription.text = "" 25 binding.textSettingDescription.text = ""
26 binding.textSettingDescription.visibility = View.GONE 26 binding.textSettingDescription.visibility = View.GONE
27 } 27 }
28 binding.switchWidget.isChecked = setting.isChecked
29 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> 28 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
30 adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) 29 adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
31 } 30 }
31 binding.switchWidget.isChecked = setting.isChecked
32 32
33 binding.switchWidget.isEnabled = setting.isEditable 33 setStyle(setting.isEditable, binding)
34 } 34 }
35 35
36 override fun onClick(clicked: View) { 36 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 6c4ddaf6b..d50c421a0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
19import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
20import androidx.core.view.WindowInsetsCompat 20import androidx.core.view.WindowInsetsCompat
21import androidx.core.view.isVisible 21import androidx.core.view.isVisible
22import androidx.core.view.updatePadding
22import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
23import androidx.fragment.app.activityViewModels 24import androidx.fragment.app.activityViewModels
24import androidx.navigation.findNavController 25import androidx.navigation.findNavController
@@ -32,10 +33,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
32import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding 33import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
33import org.yuzu.yuzu_emu.features.settings.model.Settings 34import org.yuzu.yuzu_emu.features.settings.model.Settings
34import org.yuzu.yuzu_emu.model.HomeViewModel 35import org.yuzu.yuzu_emu.model.HomeViewModel
36import org.yuzu.yuzu_emu.model.SetupCallback
35import org.yuzu.yuzu_emu.model.SetupPage 37import org.yuzu.yuzu_emu.model.SetupPage
38import org.yuzu.yuzu_emu.model.StepState
36import org.yuzu.yuzu_emu.ui.main.MainActivity 39import org.yuzu.yuzu_emu.ui.main.MainActivity
37import org.yuzu.yuzu_emu.utils.DirectoryInitialization 40import org.yuzu.yuzu_emu.utils.DirectoryInitialization
38import org.yuzu.yuzu_emu.utils.GameHelper 41import org.yuzu.yuzu_emu.utils.GameHelper
42import org.yuzu.yuzu_emu.utils.ViewUtils
39 43
40class SetupFragment : Fragment() { 44class SetupFragment : Fragment() {
41 private var _binding: FragmentSetupBinding? = null 45 private var _binding: FragmentSetupBinding? = null
@@ -112,14 +116,22 @@ class SetupFragment : Fragment() {
112 0, 116 0,
113 false, 117 false,
114 R.string.give_permission, 118 R.string.give_permission,
115 { permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }, 119 {
120 notificationCallback = it
121 permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
122 },
116 true, 123 true,
117 R.string.notification_warning, 124 R.string.notification_warning,
118 R.string.notification_warning_description, 125 R.string.notification_warning_description,
119 0, 126 0,
120 { 127 {
121 NotificationManagerCompat.from(requireContext()) 128 if (NotificationManagerCompat.from(requireContext())
122 .areNotificationsEnabled() 129 .areNotificationsEnabled()
130 ) {
131 StepState.COMPLETE
132 } else {
133 StepState.INCOMPLETE
134 }
123 } 135 }
124 ) 136 )
125 ) 137 )
@@ -133,12 +145,22 @@ class SetupFragment : Fragment() {
133 R.drawable.ic_add, 145 R.drawable.ic_add,
134 true, 146 true,
135 R.string.select_keys, 147 R.string.select_keys,
136 { mainActivity.getProdKey.launch(arrayOf("*/*")) }, 148 {
149 keyCallback = it
150 getProdKey.launch(arrayOf("*/*"))
151 },
137 true, 152 true,
138 R.string.install_prod_keys_warning, 153 R.string.install_prod_keys_warning,
139 R.string.install_prod_keys_warning_description, 154 R.string.install_prod_keys_warning_description,
140 R.string.install_prod_keys_warning_help, 155 R.string.install_prod_keys_warning_help,
141 { File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() } 156 {
157 val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
158 if (file.exists()) {
159 StepState.COMPLETE
160 } else {
161 StepState.INCOMPLETE
162 }
163 }
142 ) 164 )
143 ) 165 )
144 add( 166 add(
@@ -150,9 +172,8 @@ class SetupFragment : Fragment() {
150 true, 172 true,
151 R.string.add_games, 173 R.string.add_games,
152 { 174 {
153 mainActivity.getGamesDirectory.launch( 175 gamesDirCallback = it
154 Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data 176 getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
155 )
156 }, 177 },
157 true, 178 true,
158 R.string.add_games_warning, 179 R.string.add_games_warning,
@@ -163,7 +184,11 @@ class SetupFragment : Fragment() {
163 PreferenceManager.getDefaultSharedPreferences( 184 PreferenceManager.getDefaultSharedPreferences(
164 YuzuApplication.appContext 185 YuzuApplication.appContext
165 ) 186 )
166 preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() 187 if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
188 StepState.COMPLETE
189 } else {
190 StepState.INCOMPLETE
191 }
167 } 192 }
168 ) 193 )
169 ) 194 )
@@ -181,6 +206,13 @@ class SetupFragment : Fragment() {
181 ) 206 )
182 } 207 }
183 208
209 homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
210 if (it) {
211 pageForward()
212 homeViewModel.setShouldPageForward(false)
213 }
214 }
215
184 binding.viewPager2.apply { 216 binding.viewPager2.apply {
185 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) 217 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
186 offscreenPageLimit = 2 218 offscreenPageLimit = 2
@@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
194 super.onPageSelected(position) 226 super.onPageSelected(position)
195 227
196 if (position == 1 && previousPosition == 0) { 228 if (position == 1 && previousPosition == 0) {
197 showView(binding.buttonNext) 229 ViewUtils.showView(binding.buttonNext)
198 showView(binding.buttonBack) 230 ViewUtils.showView(binding.buttonBack)
199 } else if (position == 0 && previousPosition == 1) { 231 } else if (position == 0 && previousPosition == 1) {
200 hideView(binding.buttonBack) 232 ViewUtils.hideView(binding.buttonBack)
201 hideView(binding.buttonNext) 233 ViewUtils.hideView(binding.buttonNext)
202 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) { 234 } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
203 hideView(binding.buttonNext) 235 ViewUtils.hideView(binding.buttonNext)
204 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) { 236 } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
205 showView(binding.buttonNext) 237 ViewUtils.showView(binding.buttonNext)
206 } 238 }
207 239
208 previousPosition = position 240 previousPosition = position
@@ -215,7 +247,8 @@ class SetupFragment : Fragment() {
215 247
216 // Checks if the user has completed the task on the current page 248 // Checks if the user has completed the task on the current page
217 if (currentPage.hasWarning) { 249 if (currentPage.hasWarning) {
218 if (currentPage.taskCompleted.invoke()) { 250 val stepState = currentPage.stepCompleted.invoke()
251 if (stepState != StepState.INCOMPLETE) {
219 pageForward() 252 pageForward()
220 return@setOnClickListener 253 return@setOnClickListener
221 } 254 }
@@ -264,9 +297,15 @@ class SetupFragment : Fragment() {
264 _binding = null 297 _binding = null
265 } 298 }
266 299
300 private lateinit var notificationCallback: SetupCallback
301
267 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 302 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
268 private val permissionLauncher = 303 private val permissionLauncher =
269 registerForActivityResult(ActivityResultContracts.RequestPermission()) { 304 registerForActivityResult(ActivityResultContracts.RequestPermission()) {
305 if (it) {
306 notificationCallback.onStepCompleted()
307 }
308
270 if (!it && 309 if (!it &&
271 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) 310 !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
272 ) { 311 ) {
@@ -277,6 +316,27 @@ class SetupFragment : Fragment() {
277 } 316 }
278 } 317 }
279 318
319 private lateinit var keyCallback: SetupCallback
320
321 val getProdKey =
322 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
323 if (result != null) {
324 if (mainActivity.processKey(result)) {
325 keyCallback.onStepCompleted()
326 }
327 }
328 }
329
330 private lateinit var gamesDirCallback: SetupCallback
331
332 val getGamesDirectory =
333 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
334 if (result != null) {
335 mainActivity.processGamesDir(result)
336 gamesDirCallback.onStepCompleted()
337 }
338 }
339
280 private fun finishSetup() { 340 private fun finishSetup() {
281 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 341 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
282 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) 342 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@@ -284,33 +344,6 @@ class SetupFragment : Fragment() {
284 mainActivity.finishSetup(binding.root.findNavController()) 344 mainActivity.finishSetup(binding.root.findNavController())
285 } 345 }
286 346
287 private fun showView(view: View) {
288 view.apply {
289 alpha = 0f
290 visibility = View.VISIBLE
291 isClickable = true
292 }.animate().apply {
293 duration = 300
294 alpha(1f)
295 }.start()
296 }
297
298 private fun hideView(view: View) {
299 if (view.visibility == View.INVISIBLE) {
300 return
301 }
302
303 view.apply {
304 alpha = 1f
305 isClickable = false
306 }.animate().apply {
307 duration = 300
308 alpha(0f)
309 }.withEndAction {
310 view.visibility = View.INVISIBLE
311 }
312 }
313
314 fun pageForward() { 347 fun pageForward() {
315 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1 348 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
316 } 349 }
@@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
326 private fun setInsets() = 359 private fun setInsets() =
327 ViewCompat.setOnApplyWindowInsetsListener( 360 ViewCompat.setOnApplyWindowInsetsListener(
328 binding.root 361 binding.root
329 ) { view: View, windowInsets: WindowInsetsCompat -> 362 ) { _: View, windowInsets: WindowInsetsCompat ->
330 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 363 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
331 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) 364 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
332 view.setPadding( 365
333 barInsets.left + cutoutInsets.left, 366 val leftPadding = barInsets.left + cutoutInsets.left
334 barInsets.top + cutoutInsets.top, 367 val topPadding = barInsets.top + cutoutInsets.top
335 barInsets.right + cutoutInsets.right, 368 val rightPadding = barInsets.right + cutoutInsets.right
336 barInsets.bottom + cutoutInsets.bottom 369 val bottomPadding = barInsets.bottom + cutoutInsets.bottom
337 ) 370
371 if (resources.getBoolean(R.bool.small_layout)) {
372 binding.viewPager2
373 .updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
374 binding.constraintButtons
375 .updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
376 } else {
377 binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
378 binding.constraintButtons
379 .updatePadding(
380 left = leftPadding,
381 right = rightPadding,
382 bottom = bottomPadding
383 )
384 }
338 windowInsets 385 windowInsets
339 } 386 }
340} 387}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 263ee7144..e13d84c9c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
14 private val _statusBarShadeVisible = MutableLiveData(true) 14 private val _statusBarShadeVisible = MutableLiveData(true)
15 val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible 15 val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
16 16
17 private val _shouldPageForward = MutableLiveData(false)
18 val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
19
17 var navigatedToSetup = false 20 var navigatedToSetup = false
18 21
19 init { 22 init {
@@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
33 } 36 }
34 _statusBarShadeVisible.value = visible 37 _statusBarShadeVisible.value = visible
35 } 38 }
39
40 fun setShouldPageForward(pageForward: Boolean) {
41 _shouldPageForward.value = pageForward
42 }
36} 43}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
index a0c878e1c..09a128ae6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SetupPage.kt
@@ -10,10 +10,20 @@ data class SetupPage(
10 val buttonIconId: Int, 10 val buttonIconId: Int,
11 val leftAlignedIcon: Boolean, 11 val leftAlignedIcon: Boolean,
12 val buttonTextId: Int, 12 val buttonTextId: Int,
13 val buttonAction: () -> Unit, 13 val buttonAction: (callback: SetupCallback) -> Unit,
14 val hasWarning: Boolean, 14 val hasWarning: Boolean,
15 val warningTitleId: Int = 0, 15 val warningTitleId: Int = 0,
16 val warningDescriptionId: Int = 0, 16 val warningDescriptionId: Int = 0,
17 val warningHelpLinkId: Int = 0, 17 val warningHelpLinkId: Int = 0,
18 val taskCompleted: () -> Boolean = { true } 18 val stepCompleted: () -> StepState = { StepState.UNDEFINED }
19) 19)
20
21interface SetupCallback {
22 fun onStepCompleted()
23}
24
25enum class StepState {
26 COMPLETE,
27 INCOMPLETE,
28 UNDEFINED
29}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index f7d7aed1e..f77d06262 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -266,73 +266,80 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
266 266
267 val getGamesDirectory = 267 val getGamesDirectory =
268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> 268 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
269 if (result == null) { 269 if (result != null) {
270 return@registerForActivityResult 270 processGamesDir(result)
271 } 271 }
272 }
272 273
273 contentResolver.takePersistableUriPermission( 274 fun processGamesDir(result: Uri) {
274 result, 275 contentResolver.takePersistableUriPermission(
275 Intent.FLAG_GRANT_READ_URI_PERMISSION 276 result,
276 ) 277 Intent.FLAG_GRANT_READ_URI_PERMISSION
278 )
277 279
278 // When a new directory is picked, we currently will reset the existing games 280 // When a new directory is picked, we currently will reset the existing games
279 // database. This effectively means that only one game directory is supported. 281 // database. This effectively means that only one game directory is supported.
280 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() 282 PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
281 .putString(GameHelper.KEY_GAME_PATH, result.toString()) 283 .putString(GameHelper.KEY_GAME_PATH, result.toString())
282 .apply() 284 .apply()
283 285
284 Toast.makeText( 286 Toast.makeText(
285 applicationContext, 287 applicationContext,
286 R.string.games_dir_selected, 288 R.string.games_dir_selected,
287 Toast.LENGTH_LONG 289 Toast.LENGTH_LONG
288 ).show() 290 ).show()
289 291
290 gamesViewModel.reloadGames(true) 292 gamesViewModel.reloadGames(true)
291 } 293 }
292 294
293 val getProdKey = 295 val getProdKey =
294 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 296 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
295 if (result == null) { 297 if (result != null) {
296 return@registerForActivityResult 298 processKey(result)
297 } 299 }
300 }
298 301
299 if (FileUtil.getExtension(result) != "keys") { 302 fun processKey(result: Uri): Boolean {
300 MessageDialogFragment.newInstance( 303 if (FileUtil.getExtension(result) != "keys") {
301 R.string.reading_keys_failure, 304 MessageDialogFragment.newInstance(
302 R.string.install_prod_keys_failure_extension_description 305 R.string.reading_keys_failure,
303 ).show(supportFragmentManager, MessageDialogFragment.TAG) 306 R.string.install_prod_keys_failure_extension_description
304 return@registerForActivityResult 307 ).show(supportFragmentManager, MessageDialogFragment.TAG)
305 } 308 return false
309 }
306 310
307 contentResolver.takePersistableUriPermission( 311 contentResolver.takePersistableUriPermission(
312 result,
313 Intent.FLAG_GRANT_READ_URI_PERMISSION
314 )
315
316 val dstPath = DirectoryInitialization.userDirectory + "/keys/"
317 if (FileUtil.copyUriToInternalStorage(
318 applicationContext,
308 result, 319 result,
309 Intent.FLAG_GRANT_READ_URI_PERMISSION 320 dstPath,
321 "prod.keys"
310 ) 322 )
311 323 ) {
312 val dstPath = DirectoryInitialization.userDirectory + "/keys/" 324 if (NativeLibrary.reloadKeys()) {
313 if (FileUtil.copyUriToInternalStorage( 325 Toast.makeText(
314 applicationContext, 326 applicationContext,
315 result, 327 R.string.install_keys_success,
316 dstPath, 328 Toast.LENGTH_SHORT
317 "prod.keys" 329 ).show()
318 ) 330 gamesViewModel.reloadGames(true)
319 ) { 331 return true
320 if (NativeLibrary.reloadKeys()) { 332 } else {
321 Toast.makeText( 333 MessageDialogFragment.newInstance(
322 applicationContext, 334 R.string.invalid_keys_error,
323 R.string.install_keys_success, 335 R.string.install_keys_failure_description,
324 Toast.LENGTH_SHORT 336 R.string.dumping_keys_quickstart_link
325 ).show() 337 ).show(supportFragmentManager, MessageDialogFragment.TAG)
326 gamesViewModel.reloadGames(true) 338 return false
327 } else {
328 MessageDialogFragment.newInstance(
329 R.string.invalid_keys_error,
330 R.string.install_keys_failure_description,
331 R.string.dumping_keys_quickstart_link
332 ).show(supportFragmentManager, MessageDialogFragment.TAG)
333 }
334 } 339 }
335 } 340 }
341 return false
342 }
336 343
337 val getFirmware = 344 val getFirmware =
338 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 345 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
new file mode 100644
index 000000000..f9a3e4126
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.view.View
7
8object ViewUtils {
9 fun showView(view: View, length: Long = 300) {
10 view.apply {
11 alpha = 0f
12 visibility = View.VISIBLE
13 isClickable = true
14 }.animate().apply {
15 duration = length
16 alpha(1f)
17 }.start()
18 }
19
20 fun hideView(view: View, length: Long = 300) {
21 if (view.visibility == View.INVISIBLE) {
22 return
23 }
24
25 view.apply {
26 alpha = 1f
27 isClickable = false
28 }.animate().apply {
29 duration = length
30 alpha(0f)
31 }.withEndAction {
32 view.visibility = View.INVISIBLE
33 }.start()
34 }
35}
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
index cbe631d88..406df9eab 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 android:id="@+id/setup_root" 5 android:id="@+id/setup_root"
@@ -8,33 +8,39 @@
8 8
9 <androidx.viewpager2.widget.ViewPager2 9 <androidx.viewpager2.widget.ViewPager2
10 android:id="@+id/viewPager2" 10 android:id="@+id/viewPager2"
11 android:layout_width="0dp" 11 android:layout_width="match_parent"
12 android:layout_height="0dp" 12 android:layout_height="match_parent"
13 app:layout_constraintBottom_toBottomOf="parent" 13 android:layout_alignParentTop="true"
14 app:layout_constraintEnd_toEndOf="parent" 14 android:layout_alignParentBottom="true"
15 app:layout_constraintStart_toStartOf="parent" 15 android:clipToPadding="false" />
16 app:layout_constraintTop_toTopOf="parent" />
17 16
18 <com.google.android.material.button.MaterialButton 17 <androidx.constraintlayout.widget.ConstraintLayout
19 style="@style/Widget.Material3.Button.TextButton" 18 android:id="@+id/constraint_buttons"
20 android:id="@+id/button_next" 19 android:layout_width="match_parent"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content" 20 android:layout_height="wrap_content"
23 android:layout_margin="16dp" 21 android:layout_alignParentBottom="true"
24 android:text="@string/next" 22 android:layout_margin="8dp">
25 android:visibility="invisible"
26 app:layout_constraintBottom_toBottomOf="parent"
27 app:layout_constraintEnd_toEndOf="parent" />
28 23
29 <com.google.android.material.button.MaterialButton 24 <com.google.android.material.button.MaterialButton
30 android:id="@+id/button_back" 25 android:id="@+id/button_next"
31 style="@style/Widget.Material3.Button.TextButton" 26 style="@style/Widget.Material3.Button.TextButton"
32 android:layout_width="wrap_content" 27 android:layout_width="wrap_content"
33 android:layout_height="wrap_content" 28 android:layout_height="wrap_content"
34 android:layout_margin="16dp" 29 android:text="@string/next"
35 android:text="@string/back" 30 android:visibility="invisible"
36 android:visibility="invisible" 31 app:layout_constraintBottom_toBottomOf="parent"
37 app:layout_constraintBottom_toBottomOf="parent" 32 app:layout_constraintEnd_toEndOf="parent" />
38 app:layout_constraintStart_toStartOf="parent" /> 33
34 <com.google.android.material.button.MaterialButton
35 android:id="@+id/button_back"
36 style="@style/Widget.Material3.Button.TextButton"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:text="@string/back"
40 android:visibility="invisible"
41 app:layout_constraintBottom_toBottomOf="parent"
42 app:layout_constraintStart_toStartOf="parent" />
43
44 </androidx.constraintlayout.widget.ConstraintLayout>
39 45
40</androidx.constraintlayout.widget.ConstraintLayout> 46</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout-w600dp/page_setup.xml b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
index e1c26b2f8..9e0ab8ecb 100644
--- a/src/android/app/src/main/res/layout-w600dp/page_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/page_setup.xml
@@ -21,45 +21,76 @@
21 21
22 </LinearLayout> 22 </LinearLayout>
23 23
24 <LinearLayout 24 <androidx.constraintlayout.widget.ConstraintLayout
25 android:layout_width="match_parent" 25 android:layout_width="match_parent"
26 android:layout_height="match_parent" 26 android:layout_height="match_parent"
27 android:layout_weight="1" 27 android:layout_weight="1">
28 android:orientation="vertical"
29 android:gravity="center">
30 28
31 <com.google.android.material.textview.MaterialTextView 29 <com.google.android.material.textview.MaterialTextView
32 style="@style/TextAppearance.Material3.DisplaySmall"
33 android:id="@+id/text_title" 30 android:id="@+id/text_title"
34 android:layout_width="match_parent" 31 style="@style/TextAppearance.Material3.DisplaySmall"
35 android:layout_height="wrap_content" 32 android:layout_width="0dp"
36 android:textAlignment="center" 33 android:layout_height="0dp"
34 android:gravity="center"
37 android:textColor="?attr/colorOnSurface" 35 android:textColor="?attr/colorOnSurface"
38 android:textStyle="bold" 36 android:textStyle="bold"
37 app:layout_constraintBottom_toTopOf="@+id/text_description"
38 app:layout_constraintEnd_toEndOf="parent"
39 app:layout_constraintStart_toStartOf="parent"
40 app:layout_constraintTop_toTopOf="parent"
41 app:layout_constraintVertical_weight="2"
39 tools:text="@string/welcome" /> 42 tools:text="@string/welcome" />
40 43
41 <com.google.android.material.textview.MaterialTextView 44 <com.google.android.material.textview.MaterialTextView
42 style="@style/TextAppearance.Material3.TitleLarge"
43 android:id="@+id/text_description" 45 android:id="@+id/text_description"
44 android:layout_width="match_parent" 46 style="@style/TextAppearance.Material3.TitleLarge"
45 android:layout_height="wrap_content" 47 android:layout_width="0dp"
46 android:layout_marginTop="16dp" 48 android:layout_height="0dp"
47 android:paddingHorizontal="32dp" 49 android:gravity="center"
48 android:textAlignment="center" 50 android:textSize="20sp"
49 android:textSize="26sp" 51 android:paddingHorizontal="16dp"
50 app:lineHeight="40sp" 52 app:layout_constraintBottom_toTopOf="@+id/button_action"
53 app:layout_constraintEnd_toEndOf="parent"
54 app:layout_constraintStart_toStartOf="parent"
55 app:layout_constraintTop_toBottomOf="@+id/text_title"
56 app:layout_constraintVertical_weight="2"
57 app:lineHeight="30sp"
51 tools:text="@string/welcome_description" /> 58 tools:text="@string/welcome_description" />
52 59
60 <com.google.android.material.textview.MaterialTextView
61 android:id="@+id/text_confirmation"
62 style="@style/TextAppearance.Material3.TitleLarge"
63 android:layout_width="0dp"
64 android:layout_height="0dp"
65 android:paddingHorizontal="16dp"
66 android:paddingBottom="20dp"
67 android:gravity="center"
68 android:textSize="30sp"
69 android:visibility="invisible"
70 android:text="@string/step_complete"
71 android:textStyle="bold"
72 app:layout_constraintBottom_toBottomOf="parent"
73 app:layout_constraintEnd_toEndOf="parent"
74 app:layout_constraintStart_toStartOf="parent"
75 app:layout_constraintTop_toBottomOf="@+id/text_description"
76 app:layout_constraintVertical_weight="1"
77 app:lineHeight="30sp" />
78
53 <com.google.android.material.button.MaterialButton 79 <com.google.android.material.button.MaterialButton
54 android:id="@+id/button_action" 80 android:id="@+id/button_action"
55 android:layout_width="wrap_content" 81 android:layout_width="wrap_content"
56 android:layout_height="56dp" 82 android:layout_height="56dp"
57 android:layout_marginTop="32dp" 83 android:layout_marginTop="16dp"
84 android:layout_marginBottom="48dp"
58 android:textSize="20sp" 85 android:textSize="20sp"
59 app:iconSize="24sp"
60 app:iconGravity="end" 86 app:iconGravity="end"
87 app:iconSize="24sp"
88 app:layout_constraintBottom_toBottomOf="parent"
89 app:layout_constraintEnd_toEndOf="parent"
90 app:layout_constraintStart_toStartOf="parent"
91 app:layout_constraintTop_toBottomOf="@+id/text_description"
61 tools:text="Get started" /> 92 tools:text="Get started" />
62 93
63 </LinearLayout> 94 </androidx.constraintlayout.widget.ConstraintLayout>
64 95
65</LinearLayout> 96</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/dialog_slider.xml b/src/android/app/src/main/res/layout/dialog_slider.xml
index 8c84cb606..d1cb31739 100644
--- a/src/android/app/src/main/res/layout/dialog_slider.xml
+++ b/src/android/app/src/main/res/layout/dialog_slider.xml
@@ -5,23 +5,16 @@
5 android:layout_height="wrap_content" 5 android:layout_height="wrap_content"
6 android:orientation="vertical"> 6 android:orientation="vertical">
7 7
8 <TextView 8 <com.google.android.material.textview.MaterialTextView
9 android:id="@+id/text_value" 9 android:id="@+id/text_value"
10 style="@style/TextAppearance.Material3.LabelMedium"
10 android:layout_width="wrap_content" 11 android:layout_width="wrap_content"
11 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
12 android:layout_alignParentTop="true" 13 android:layout_alignParentTop="true"
13 android:layout_centerHorizontal="true" 14 android:layout_centerHorizontal="true"
14 android:layout_marginBottom="@dimen/spacing_medlarge" 15 android:layout_marginBottom="@dimen/spacing_medlarge"
15 android:layout_marginTop="@dimen/spacing_medlarge" 16 android:layout_marginTop="@dimen/spacing_medlarge"
16 tools:text="75" /> 17 tools:text="75%" />
17
18 <TextView
19 android:id="@+id/text_units"
20 android:layout_width="wrap_content"
21 android:layout_height="wrap_content"
22 android:layout_alignTop="@+id/text_value"
23 android:layout_toEndOf="@+id/text_value"
24 tools:text="%" />
25 18
26 <com.google.android.material.slider.Slider 19 <com.google.android.material.slider.Slider
27 android:id="@+id/slider" 20 android:id="@+id/slider"
diff --git a/src/android/app/src/main/res/layout/fragment_setup.xml b/src/android/app/src/main/res/layout/fragment_setup.xml
index d7bafaea2..9499f6463 100644
--- a/src/android/app/src/main/res/layout/fragment_setup.xml
+++ b/src/android/app/src/main/res/layout/fragment_setup.xml
@@ -1,5 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 android:id="@+id/setup_root" 5 android:id="@+id/setup_root"
@@ -8,35 +8,39 @@
8 8
9 <androidx.viewpager2.widget.ViewPager2 9 <androidx.viewpager2.widget.ViewPager2
10 android:id="@+id/viewPager2" 10 android:id="@+id/viewPager2"
11 android:layout_width="0dp" 11 android:layout_width="match_parent"
12 android:layout_height="0dp"
13 android:clipToPadding="false"
14 android:layout_marginBottom="16dp"
15 app:layout_constraintBottom_toTopOf="@+id/button_next"
16 app:layout_constraintEnd_toEndOf="parent"
17 app:layout_constraintStart_toStartOf="parent"
18 app:layout_constraintTop_toTopOf="parent" />
19
20 <com.google.android.material.button.MaterialButton
21 style="@style/Widget.Material3.Button.TextButton"
22 android:id="@+id/button_next"
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content" 12 android:layout_height="wrap_content"
25 android:layout_margin="12dp" 13 android:layout_above="@+id/constraint_buttons"
26 android:text="@string/next" 14 android:layout_alignParentTop="true"
27 android:visibility="invisible" 15 android:clipToPadding="false" />
28 app:layout_constraintBottom_toBottomOf="parent"
29 app:layout_constraintEnd_toEndOf="parent" />
30 16
31 <com.google.android.material.button.MaterialButton 17 <androidx.constraintlayout.widget.ConstraintLayout
32 style="@style/Widget.Material3.Button.TextButton" 18 android:id="@+id/constraint_buttons"
33 android:id="@+id/button_back" 19 android:layout_width="match_parent"
34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content" 20 android:layout_height="wrap_content"
36 android:layout_margin="12dp" 21 android:layout_margin="8dp"
37 android:text="@string/back" 22 android:layout_alignParentBottom="true">
38 android:visibility="invisible" 23
39 app:layout_constraintBottom_toBottomOf="parent" 24 <com.google.android.material.button.MaterialButton
40 app:layout_constraintStart_toStartOf="parent" /> 25 android:id="@+id/button_next"
26 style="@style/Widget.Material3.Button.TextButton"
27 android:layout_width="wrap_content"
28 android:layout_height="wrap_content"
29 android:text="@string/next"
30 android:visibility="invisible"
31 app:layout_constraintBottom_toBottomOf="parent"
32 app:layout_constraintEnd_toEndOf="parent" />
33
34 <com.google.android.material.button.MaterialButton
35 android:id="@+id/button_back"
36 style="@style/Widget.Material3.Button.TextButton"
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:text="@string/back"
40 android:visibility="invisible"
41 app:layout_constraintBottom_toBottomOf="parent"
42 app:layout_constraintStart_toStartOf="parent" />
43
44 </androidx.constraintlayout.widget.ConstraintLayout>
41 45
42</androidx.constraintlayout.widget.ConstraintLayout> 46</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index ec896342b..f1037a740 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -1,9 +1,10 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2<RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
3 xmlns:tools="http://schemas.android.com/tools" 5 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent" 6 android:layout_width="match_parent"
5 android:layout_height="wrap_content" 7 android:layout_height="wrap_content"
6 xmlns:app="http://schemas.android.com/apk/res-auto"
7 android:background="?android:attr/selectableItemBackground" 8 android:background="?android:attr/selectableItemBackground"
8 android:clickable="true" 9 android:clickable="true"
9 android:focusable="true" 10 android:focusable="true"
@@ -11,31 +12,40 @@
11 android:minHeight="72dp" 12 android:minHeight="72dp"
12 android:padding="@dimen/spacing_large"> 13 android:padding="@dimen/spacing_large">
13 14
14 <com.google.android.material.textview.MaterialTextView 15 <LinearLayout
15 style="@style/TextAppearance.Material3.HeadlineMedium" 16 android:layout_width="match_parent"
16 android:id="@+id/text_setting_name"
17 android:layout_width="0dp"
18 android:layout_height="wrap_content" 17 android:layout_height="wrap_content"
19 android:layout_alignParentEnd="true" 18 android:orientation="vertical">
20 android:layout_alignParentStart="true"
21 android:layout_alignParentTop="true"
22 android:textSize="16sp"
23 android:textAlignment="viewStart"
24 app:lineHeight="28dp"
25 tools:text="Setting Name" />
26 19
27 <TextView 20 <com.google.android.material.textview.MaterialTextView
28 style="@style/TextAppearance.Material3.BodySmall" 21 android:id="@+id/text_setting_name"
29 android:id="@+id/text_setting_description" 22 style="@style/TextAppearance.Material3.HeadlineMedium"
30 android:layout_width="wrap_content" 23 android:layout_width="match_parent"
31 android:layout_height="wrap_content" 24 android:layout_height="wrap_content"
32 android:layout_alignParentEnd="true" 25 android:textAlignment="viewStart"
33 android:layout_alignParentStart="true" 26 android:textSize="16sp"
34 android:layout_alignStart="@+id/text_setting_name" 27 app:lineHeight="22dp"
35 android:layout_below="@+id/text_setting_name" 28 tools:text="Setting Name" />
36 android:layout_marginTop="@dimen/spacing_small" 29
37 android:visibility="visible" 30 <com.google.android.material.textview.MaterialTextView
38 android:textAlignment="viewStart" 31 android:id="@+id/text_setting_description"
39 tools:text="@string/app_disclaimer" /> 32 style="@style/TextAppearance.Material3.BodySmall"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:layout_marginTop="@dimen/spacing_small"
36 android:textAlignment="viewStart"
37 tools:text="@string/app_disclaimer" />
38
39 <com.google.android.material.textview.MaterialTextView
40 android:id="@+id/text_setting_value"
41 style="@style/TextAppearance.Material3.LabelMedium"
42 android:layout_width="match_parent"
43 android:layout_height="wrap_content"
44 android:layout_marginTop="@dimen/spacing_small"
45 android:textAlignment="viewStart"
46 android:textStyle="bold"
47 tools:text="1x" />
48
49 </LinearLayout>
40 50
41</RelativeLayout> 51</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/page_setup.xml b/src/android/app/src/main/res/layout/page_setup.xml
index 1436ef308..535abcf02 100644
--- a/src/android/app/src/main/res/layout/page_setup.xml
+++ b/src/android/app/src/main/res/layout/page_setup.xml
@@ -21,11 +21,12 @@
21 app:layout_constraintVertical_chainStyle="spread" 21 app:layout_constraintVertical_chainStyle="spread"
22 app:layout_constraintWidth_max="220dp" 22 app:layout_constraintWidth_max="220dp"
23 app:layout_constraintWidth_min="110dp" 23 app:layout_constraintWidth_min="110dp"
24 app:layout_constraintVertical_weight="3" /> 24 app:layout_constraintVertical_weight="3"
25 tools:src="@drawable/ic_notification" />
25 26
26 <com.google.android.material.textview.MaterialTextView 27 <com.google.android.material.textview.MaterialTextView
27 android:id="@+id/text_title" 28 android:id="@+id/text_title"
28 style="@style/TextAppearance.Material3.DisplayMedium" 29 style="@style/TextAppearance.Material3.DisplaySmall"
29 android:layout_width="0dp" 30 android:layout_width="0dp"
30 android:layout_height="0dp" 31 android:layout_height="0dp"
31 android:textAlignment="center" 32 android:textAlignment="center"
@@ -44,23 +45,42 @@
44 android:layout_width="0dp" 45 android:layout_width="0dp"
45 android:layout_height="0dp" 46 android:layout_height="0dp"
46 android:textAlignment="center" 47 android:textAlignment="center"
47 android:textSize="26sp" 48 android:textSize="20sp"
48 android:paddingHorizontal="16dp" 49 android:paddingHorizontal="16dp"
49 app:layout_constraintBottom_toTopOf="@+id/button_action" 50 app:layout_constraintBottom_toTopOf="@+id/button_action"
50 app:layout_constraintEnd_toEndOf="parent" 51 app:layout_constraintEnd_toEndOf="parent"
51 app:layout_constraintStart_toStartOf="parent" 52 app:layout_constraintStart_toStartOf="parent"
52 app:layout_constraintTop_toBottomOf="@+id/text_title" 53 app:layout_constraintTop_toBottomOf="@+id/text_title"
53 app:layout_constraintVertical_weight="2" 54 app:layout_constraintVertical_weight="2"
54 app:lineHeight="40sp" 55 app:lineHeight="30sp"
55 tools:text="@string/welcome_description" /> 56 tools:text="@string/welcome_description" />
56 57
58 <com.google.android.material.textview.MaterialTextView
59 android:id="@+id/text_confirmation"
60 style="@style/TextAppearance.Material3.TitleLarge"
61 android:layout_width="wrap_content"
62 android:layout_height="0dp"
63 android:paddingHorizontal="16dp"
64 android:paddingTop="24dp"
65 android:textAlignment="center"
66 android:textSize="30sp"
67 android:visibility="invisible"
68 android:text="@string/step_complete"
69 android:textStyle="bold"
70 app:layout_constraintBottom_toBottomOf="parent"
71 app:layout_constraintEnd_toEndOf="parent"
72 app:layout_constraintStart_toStartOf="parent"
73 app:layout_constraintTop_toBottomOf="@+id/text_description"
74 app:layout_constraintVertical_weight="1"
75 app:lineHeight="30sp" />
76
57 <com.google.android.material.button.MaterialButton 77 <com.google.android.material.button.MaterialButton
58 android:id="@+id/button_action" 78 android:id="@+id/button_action"
59 android:layout_width="wrap_content" 79 android:layout_width="wrap_content"
60 android:layout_height="56dp" 80 android:layout_height="56dp"
61 android:textSize="20sp"
62 android:layout_marginTop="16dp" 81 android:layout_marginTop="16dp"
63 android:layout_marginBottom="48dp" 82 android:layout_marginBottom="48dp"
83 android:textSize="20sp"
64 app:iconGravity="end" 84 app:iconGravity="end"
65 app:iconSize="24sp" 85 app:iconSize="24sp"
66 app:layout_constraintBottom_toBottomOf="parent" 86 app:layout_constraintBottom_toBottomOf="parent"
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 02e25504d..de1b2909b 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
29 <string name="back">Back</string> 29 <string name="back">Back</string>
30 <string name="add_games">Add Games</string> 30 <string name="add_games">Add Games</string>
31 <string name="add_games_description">Select your games folder</string> 31 <string name="add_games_description">Select your games folder</string>
32 <string name="step_complete">Complete!</string>
32 33
33 <!-- Home strings --> 34 <!-- Home strings -->
34 <string name="home_games">Games</string> 35 <string name="home_games">Games</string>
@@ -149,6 +150,7 @@
149 <string name="frame_limit_slider">Limit speed percent</string> 150 <string name="frame_limit_slider">Limit speed percent</string>
150 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> 151 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
151 <string name="cpu_accuracy">CPU accuracy</string> 152 <string name="cpu_accuracy">CPU accuracy</string>
153 <string name="value_with_units">%1$s%2$s</string>
152 154
153 <!-- System settings strings --> 155 <!-- System settings strings -->
154 <string name="use_docked_mode">Docked Mode</string> 156 <string name="use_docked_mode">Docked Mode</string>
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 0f839d5b4..e55831f27 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -263,6 +263,23 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
263 263
264 std::vector<u8> mem(size); 264 std::vector<u8> mem(size);
265 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { 265 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
266 // Restore any bytes belonging to replaced instructions.
267 auto it = replaced_instructions.lower_bound(addr);
268 for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
269 // Get the bytes of the instruction we previously replaced.
270 const u32 original_bytes = it->second;
271
272 // Calculate where to start writing to the output buffer.
273 const size_t output_offset = it->first - addr;
274
275 // Calculate how many bytes to write.
276 // The loop condition ensures output_offset < size.
277 const size_t n = std::min<size_t>(size - output_offset, sizeof(u32));
278
279 // Write the bytes to the output buffer.
280 std::memcpy(mem.data() + output_offset, &original_bytes, n);
281 }
282
266 SendReply(Common::HexToString(mem)); 283 SendReply(Common::HexToString(mem));
267 } else { 284 } else {
268 SendReply(GDB_STUB_REPLY_ERR); 285 SendReply(GDB_STUB_REPLY_ERR);
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 6b35f448c..00beb40dd 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -289,6 +289,19 @@ enum class GyroscopeZeroDriftMode : u32 {
289 Tight = 2, 289 Tight = 2,
290}; 290};
291 291
292// This is nn::settings::system::TouchScreenMode
293enum class TouchScreenMode : u32 {
294 Stylus = 0,
295 Standard = 1,
296};
297
298// This is nn::hid::TouchScreenModeForNx
299enum class TouchScreenModeForNx : u8 {
300 UseSystemSetting,
301 Finger,
302 Heat2,
303};
304
292// This is nn::hid::NpadStyleTag 305// This is nn::hid::NpadStyleTag
293struct NpadStyleTag { 306struct NpadStyleTag {
294 union { 307 union {
@@ -334,6 +347,14 @@ struct TouchState {
334}; 347};
335static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 348static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
336 349
350// This is nn::hid::TouchScreenConfigurationForNx
351struct TouchScreenConfigurationForNx {
352 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
353 INSERT_PADDING_BYTES(0xF);
354};
355static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
356 "TouchScreenConfigurationForNx is an invalid size");
357
337struct NpadColor { 358struct NpadColor {
338 u8 r{}; 359 u8 r{};
339 u8 g{}; 360 u8 g{};
@@ -662,6 +683,11 @@ struct MouseState {
662}; 683};
663static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); 684static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
664 685
686struct UniquePadId {
687 u64 id;
688};
689static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
690
665/// Converts a NpadIdType to an array index. 691/// Converts a NpadIdType to an array index.
666constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { 692constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
667 switch (npad_id_type) { 693 switch (npad_id_type) {
diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h
index 00bef6ea1..27f43cd19 100644
--- a/src/core/hle/kernel/k_hardware_timer.h
+++ b/src/core/hle/kernel/k_hardware_timer.h
@@ -19,13 +19,7 @@ public:
19 void Initialize(); 19 void Initialize();
20 void Finalize(); 20 void Finalize();
21 21
22 s64 GetCount() const { 22 s64 GetTick() const;
23 return GetTick();
24 }
25
26 void RegisterTask(KTimerTask* task, s64 time_from_now) {
27 this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
28 }
29 23
30 void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) { 24 void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
31 KScopedDisableDispatch dd{m_kernel}; 25 KScopedDisableDispatch dd{m_kernel};
@@ -42,7 +36,6 @@ private:
42 void EnableInterrupt(s64 wakeup_time); 36 void EnableInterrupt(s64 wakeup_time);
43 void DisableInterrupt(); 37 void DisableInterrupt();
44 bool GetInterruptEnabled(); 38 bool GetInterruptEnabled();
45 s64 GetTick() const;
46 void DoTask(); 39 void DoTask();
47 40
48private: 41private:
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index fcee26a29..d8a63aaf8 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -5,6 +5,7 @@
5#include "common/overflow.h" 5#include "common/overflow.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/kernel/k_hardware_timer.h"
8#include "core/hle/kernel/k_resource_limit.h" 9#include "core/hle/kernel/k_resource_limit.h"
9#include "core/hle/kernel/svc_results.h" 10#include "core/hle/kernel/svc_results.h"
10 11
@@ -15,9 +16,7 @@ KResourceLimit::KResourceLimit(KernelCore& kernel)
15 : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{m_kernel}, m_cond_var{m_kernel} {} 16 : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{m_kernel}, m_cond_var{m_kernel} {}
16KResourceLimit::~KResourceLimit() = default; 17KResourceLimit::~KResourceLimit() = default;
17 18
18void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing) { 19void KResourceLimit::Initialize() {}
19 m_core_timing = core_timing;
20}
21 20
22void KResourceLimit::Finalize() {} 21void KResourceLimit::Finalize() {}
23 22
@@ -86,7 +85,7 @@ Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
86} 85}
87 86
88bool KResourceLimit::Reserve(LimitableResource which, s64 value) { 87bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
89 return Reserve(which, value, m_core_timing->GetGlobalTimeNs().count() + DefaultTimeout); 88 return Reserve(which, value, m_kernel.HardwareTimer().GetTick() + DefaultTimeout);
90} 89}
91 90
92bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { 91bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
@@ -117,7 +116,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
117 } 116 }
118 117
119 if (m_current_hints[index] + value <= m_limit_values[index] && 118 if (m_current_hints[index] + value <= m_limit_values[index] &&
120 (timeout < 0 || m_core_timing->GetGlobalTimeNs().count() < timeout)) { 119 (timeout < 0 || m_kernel.HardwareTimer().GetTick() < timeout)) {
121 m_waiter_count++; 120 m_waiter_count++;
122 m_cond_var.Wait(std::addressof(m_lock), timeout, false); 121 m_cond_var.Wait(std::addressof(m_lock), timeout, false);
123 m_waiter_count--; 122 m_waiter_count--;
@@ -154,7 +153,7 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
154 153
155KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) { 154KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
156 auto* resource_limit = KResourceLimit::Create(system.Kernel()); 155 auto* resource_limit = KResourceLimit::Create(system.Kernel());
157 resource_limit->Initialize(std::addressof(system.CoreTiming())); 156 resource_limit->Initialize();
158 157
159 // Initialize default resource limit values. 158 // Initialize default resource limit values.
160 // TODO(bunnei): These values are the system defaults, the limits for service processes are 159 // TODO(bunnei): These values are the system defaults, the limits for service processes are
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 15e69af56..b733ec8f8 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -31,7 +31,7 @@ public:
31 explicit KResourceLimit(KernelCore& kernel); 31 explicit KResourceLimit(KernelCore& kernel);
32 ~KResourceLimit() override; 32 ~KResourceLimit() override;
33 33
34 void Initialize(const Core::Timing::CoreTiming* core_timing); 34 void Initialize();
35 void Finalize() override; 35 void Finalize() override;
36 36
37 s64 GetLimitValue(LimitableResource which) const; 37 s64 GetLimitValue(LimitableResource which) const;
@@ -57,7 +57,6 @@ private:
57 mutable KLightLock m_lock; 57 mutable KLightLock m_lock;
58 s32 m_waiter_count{}; 58 s32 m_waiter_count{};
59 KLightConditionVariable m_cond_var; 59 KLightConditionVariable m_cond_var;
60 const Core::Timing::CoreTiming* m_core_timing{};
61}; 60};
62 61
63KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size); 62KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index c485022f5..b62415da7 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -28,7 +28,7 @@ public:
28 ~KScopedSchedulerLockAndSleep() { 28 ~KScopedSchedulerLockAndSleep() {
29 // Register the sleep. 29 // Register the sleep.
30 if (m_timeout_tick > 0) { 30 if (m_timeout_tick > 0) {
31 m_timer->RegisterTask(m_thread, m_timeout_tick); 31 m_timer->RegisterAbsoluteTask(m_thread, m_timeout_tick);
32 } 32 }
33 33
34 // Unlock the scheduler. 34 // Unlock the scheduler.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index ebe7582c6..a1134b7e2 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -231,7 +231,7 @@ struct KernelCore::Impl {
231 void InitializeSystemResourceLimit(KernelCore& kernel, 231 void InitializeSystemResourceLimit(KernelCore& kernel,
232 const Core::Timing::CoreTiming& core_timing) { 232 const Core::Timing::CoreTiming& core_timing) {
233 system_resource_limit = KResourceLimit::Create(system.Kernel()); 233 system_resource_limit = KResourceLimit::Create(system.Kernel());
234 system_resource_limit->Initialize(&core_timing); 234 system_resource_limit->Initialize();
235 KResourceLimit::Register(kernel, system_resource_limit); 235 KResourceLimit::Register(kernel, system_resource_limit);
236 236
237 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; 237 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
index 04cc5ea64..90ee43521 100644
--- a/src/core/hle/kernel/svc/svc_address_arbiter.cpp
+++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_hardware_timer.h"
5#include "core/hle/kernel/k_memory_layout.h" 6#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h" 7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
@@ -52,7 +53,7 @@ Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_typ
52 if (timeout_ns > 0) { 53 if (timeout_ns > 0) {
53 const s64 offset_tick(timeout_ns); 54 const s64 offset_tick(timeout_ns);
54 if (offset_tick > 0) { 55 if (offset_tick > 0) {
55 timeout = offset_tick + 2; 56 timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
56 if (timeout <= 0) { 57 if (timeout <= 0) {
57 timeout = std::numeric_limits<s64>::max(); 58 timeout = std::numeric_limits<s64>::max();
58 } 59 }
diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp
index ca120d67e..bb678e6c5 100644
--- a/src/core/hle/kernel/svc/svc_condition_variable.cpp
+++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_hardware_timer.h"
5#include "core/hle/kernel/k_memory_layout.h" 6#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h" 7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/kernel.h"
@@ -25,7 +26,7 @@ Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u
25 if (timeout_ns > 0) { 26 if (timeout_ns > 0) {
26 const s64 offset_tick(timeout_ns); 27 const s64 offset_tick(timeout_ns);
27 if (offset_tick > 0) { 28 if (offset_tick > 0) {
28 timeout = offset_tick + 2; 29 timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
29 if (timeout <= 0) { 30 if (timeout <= 0) {
30 timeout = std::numeric_limits<s64>::max(); 31 timeout = std::numeric_limits<s64>::max();
31 } 32 }
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 373ae7c8d..6b5e1cb8d 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -5,6 +5,7 @@
5#include "common/scratch_buffer.h" 5#include "common/scratch_buffer.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_hardware_timer.h"
8#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
9#include "core/hle/kernel/k_server_session.h" 10#include "core/hle/kernel/k_server_session.h"
10#include "core/hle/kernel/svc.h" 11#include "core/hle/kernel/svc.h"
@@ -82,12 +83,29 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
82 R_TRY(session->SendReply()); 83 R_TRY(session->SendReply());
83 } 84 }
84 85
86 // Convert the timeout from nanoseconds to ticks.
87 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
88 s64 timeout;
89 if (timeout_ns > 0) {
90 const s64 offset_tick(timeout_ns);
91 if (offset_tick > 0) {
92 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
93 if (timeout <= 0) {
94 timeout = std::numeric_limits<s64>::max();
95 }
96 } else {
97 timeout = std::numeric_limits<s64>::max();
98 }
99 } else {
100 timeout = timeout_ns;
101 }
102
85 // Wait for a message. 103 // Wait for a message.
86 while (true) { 104 while (true) {
87 // Wait for an object. 105 // Wait for an object.
88 s32 index; 106 s32 index;
89 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), 107 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
90 num_handles, timeout_ns); 108 num_handles, timeout);
91 if (result == ResultTimedOut) { 109 if (result == ResultTimedOut) {
92 R_RETURN(result); 110 R_RETURN(result);
93 } 111 }
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
index 732bc017e..c8e820b6a 100644
--- a/src/core/hle/kernel/svc/svc_resource_limit.cpp
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -21,7 +21,7 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
21 SCOPE_EXIT({ resource_limit->Close(); }); 21 SCOPE_EXIT({ resource_limit->Close(); });
22 22
23 // Initialize the resource limit. 23 // Initialize the resource limit.
24 resource_limit->Initialize(std::addressof(system.CoreTiming())); 24 resource_limit->Initialize();
25 25
26 // Register the limit. 26 // Register the limit.
27 KResourceLimit::Register(kernel, resource_limit); 27 KResourceLimit::Register(kernel, resource_limit);
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
index 366e8ed4a..8ebc1bd1c 100644
--- a/src/core/hle/kernel/svc/svc_synchronization.cpp
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -4,6 +4,7 @@
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "common/scratch_buffer.h" 5#include "common/scratch_buffer.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/kernel/k_hardware_timer.h"
7#include "core/hle/kernel/k_process.h" 8#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_readable_event.h" 9#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/kernel/svc.h" 10#include "core/hle/kernel/svc.h"
@@ -83,9 +84,20 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
83 } 84 }
84 }); 85 });
85 86
87 // Convert the timeout from nanoseconds to ticks.
88 s64 timeout;
89 if (timeout_ns > 0) {
90 u64 ticks = kernel.HardwareTimer().GetTick();
91 ticks += timeout_ns;
92 ticks += 2;
93
94 timeout = ticks;
95 } else {
96 timeout = timeout_ns;
97 }
98
86 // Wait on the objects. 99 // Wait on the objects.
87 Result res = 100 Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout);
88 KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns);
89 101
90 R_SUCCEED_IF(res == ResultSessionClosed); 102 R_SUCCEED_IF(res == ResultSessionClosed);
91 R_RETURN(res); 103 R_RETURN(res);
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 92bcea72b..933b82e30 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -4,6 +4,7 @@
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "core/core.h" 5#include "core/core.h"
6#include "core/core_timing.h" 6#include "core/core_timing.h"
7#include "core/hle/kernel/k_hardware_timer.h"
7#include "core/hle/kernel/k_process.h" 8#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h" 9#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -42,9 +43,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
42 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); 43 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
43 44
44 // Reserve a new thread from the process resource limit (waiting up to 100ms). 45 // Reserve a new thread from the process resource limit (waiting up to 100ms).
45 KScopedResourceReservation thread_reservation( 46 KScopedResourceReservation thread_reservation(std::addressof(process),
46 std::addressof(process), LimitableResource::ThreadCountMax, 1, 47 LimitableResource::ThreadCountMax, 1,
47 system.CoreTiming().GetGlobalTimeNs().count() + 100000000); 48 kernel.HardwareTimer().GetTick() + 100000000);
48 R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); 49 R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached);
49 50
50 // Create the thread. 51 // Create the thread.
@@ -102,20 +103,31 @@ void ExitThread(Core::System& system) {
102} 103}
103 104
104/// Sleep the current thread 105/// Sleep the current thread
105void SleepThread(Core::System& system, s64 nanoseconds) { 106void SleepThread(Core::System& system, s64 ns) {
106 auto& kernel = system.Kernel(); 107 auto& kernel = system.Kernel();
107 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); 108 const auto yield_type = static_cast<Svc::YieldType>(ns);
108 109
109 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 110 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", ns);
110 111
111 // When the input tick is positive, sleep. 112 // When the input tick is positive, sleep.
112 if (nanoseconds > 0) { 113 if (ns > 0) {
113 // Convert the timeout from nanoseconds to ticks. 114 // Convert the timeout from nanoseconds to ticks.
114 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... 115 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
116 s64 timeout;
117
118 const s64 offset_tick(ns);
119 if (offset_tick > 0) {
120 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
121 if (timeout <= 0) {
122 timeout = std::numeric_limits<s64>::max();
123 }
124 } else {
125 timeout = std::numeric_limits<s64>::max();
126 }
115 127
116 // Sleep. 128 // Sleep.
117 // NOTE: Nintendo does not check the result of this sleep. 129 // NOTE: Nintendo does not check the result of this sleep.
118 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); 130 static_cast<void>(GetCurrentThread(kernel).Sleep(timeout));
119 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { 131 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
120 KScheduler::YieldWithoutCoreMigration(kernel); 132 KScheduler::YieldWithoutCoreMigration(kernel);
121 } else if (yield_type == Svc::YieldType::WithCoreMigration) { 133 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
@@ -124,7 +136,6 @@ void SleepThread(Core::System& system, s64 nanoseconds) {
124 KScheduler::YieldToAnyThread(kernel); 136 KScheduler::YieldToAnyThread(kernel);
125 } else { 137 } else {
126 // Nintendo does nothing at all if an otherwise invalid value is passed. 138 // Nintendo does nothing at all if an otherwise invalid value is passed.
127 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
128 } 139 }
129} 140}
130 141
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index e57a3a80e..dd00921fd 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -16,22 +16,6 @@ class EmulatedConsole;
16namespace Service::HID { 16namespace Service::HID {
17class Controller_Touchscreen final : public ControllerBase { 17class Controller_Touchscreen final : public ControllerBase {
18public: 18public:
19 // This is nn::hid::TouchScreenModeForNx
20 enum class TouchScreenModeForNx : u8 {
21 UseSystemSetting,
22 Finger,
23 Heat2,
24 };
25
26 // This is nn::hid::TouchScreenConfigurationForNx
27 struct TouchScreenConfigurationForNx {
28 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
29 INSERT_PADDING_BYTES_NOINIT(0x7);
30 INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
31 };
32 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
33 "TouchScreenConfigurationForNx is an invalid size");
34
35 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); 19 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
36 ~Controller_Touchscreen() override; 20 ~Controller_Touchscreen() override;
37 21
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2bf1d8a27..fd466db7b 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2368,7 +2368,7 @@ void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
2368 2368
2369void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { 2369void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2370 IPC::RequestParser rp{ctx}; 2370 IPC::RequestParser rp{ctx};
2371 const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()}; 2371 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2372 const auto applet_resource_user_id{rp.Pop<u64>()}; 2372 const auto applet_resource_user_id{rp.Pop<u64>()};
2373 2373
2374 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", 2374 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
@@ -2543,7 +2543,8 @@ public:
2543 2543
2544class HidSys final : public ServiceFramework<HidSys> { 2544class HidSys final : public ServiceFramework<HidSys> {
2545public: 2545public:
2546 explicit HidSys(Core::System& system_) : ServiceFramework{system_, "hid:sys"} { 2546 explicit HidSys(Core::System& system_)
2547 : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"} {
2547 // clang-format off 2548 // clang-format off
2548 static const FunctionInfo functions[] = { 2549 static const FunctionInfo functions[] = {
2549 {31, nullptr, "SendKeyboardLockKeyEvent"}, 2550 {31, nullptr, "SendKeyboardLockKeyEvent"},
@@ -2568,7 +2569,7 @@ public:
2568 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, 2569 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
2569 {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, 2570 {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
2570 {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, 2571 {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
2571 {306, nullptr, "GetLastActiveNpad"}, 2572 {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
2572 {307, nullptr, "GetNpadSystemExtStyle"}, 2573 {307, nullptr, "GetNpadSystemExtStyle"},
2573 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 2574 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
2574 {309, nullptr, "GetNpadFullKeyGripColor"}, 2575 {309, nullptr, "GetNpadFullKeyGripColor"},
@@ -2624,7 +2625,7 @@ public:
2624 {700, nullptr, "ActivateUniquePad"}, 2625 {700, nullptr, "ActivateUniquePad"},
2625 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 2626 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
2626 {703, nullptr, "GetUniquePadIds"}, 2627 {703, nullptr, "GetUniquePadIds"},
2627 {751, nullptr, "AcquireJoyDetachOnBluetoothOffEventHandle"}, 2628 {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
2628 {800, nullptr, "ListSixAxisSensorHandles"}, 2629 {800, nullptr, "ListSixAxisSensorHandles"},
2629 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, 2630 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
2630 {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, 2631 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
@@ -2650,7 +2651,7 @@ public:
2650 {830, nullptr, "SetNotificationLedPattern"}, 2651 {830, nullptr, "SetNotificationLedPattern"},
2651 {831, nullptr, "SetNotificationLedPatternWithTimeout"}, 2652 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
2652 {832, nullptr, "PrepareHidsForNotificationWake"}, 2653 {832, nullptr, "PrepareHidsForNotificationWake"},
2653 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 2654 {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
2654 {851, nullptr, "EnableUsbFullKeyController"}, 2655 {851, nullptr, "EnableUsbFullKeyController"},
2655 {852, nullptr, "IsUsbConnected"}, 2656 {852, nullptr, "IsUsbConnected"},
2656 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, 2657 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
@@ -2682,7 +2683,7 @@ public:
2682 {1150, nullptr, "SetTouchScreenMagnification"}, 2683 {1150, nullptr, "SetTouchScreenMagnification"},
2683 {1151, nullptr, "GetTouchScreenFirmwareVersion"}, 2684 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
2684 {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, 2685 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
2685 {1153, nullptr, "GetTouchScreenDefaultConfiguration"}, 2686 {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
2686 {1154, nullptr, "IsFirmwareAvailableForNotification"}, 2687 {1154, nullptr, "IsFirmwareAvailableForNotification"},
2687 {1155, nullptr, "SetForceHandheldStyleVibration"}, 2688 {1155, nullptr, "SetForceHandheldStyleVibration"},
2688 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, 2689 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
@@ -2749,6 +2750,8 @@ public:
2749 // clang-format on 2750 // clang-format on
2750 2751
2751 RegisterHandlers(functions); 2752 RegisterHandlers(functions);
2753
2754 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
2752 } 2755 }
2753 2756
2754private: 2757private:
@@ -2760,17 +2763,66 @@ private:
2760 rb.Push(ResultSuccess); 2763 rb.Push(ResultSuccess);
2761 } 2764 }
2762 2765
2766 void GetLastActiveNpad(HLERequestContext& ctx) {
2767 LOG_DEBUG(Service_HID, "(STUBBED) called");
2768
2769 IPC::ResponseBuilder rb{ctx, 3};
2770 rb.Push(ResultSuccess);
2771 rb.PushEnum(Core::HID::NpadIdType::Handheld);
2772 }
2773
2763 void GetUniquePadsFromNpad(HLERequestContext& ctx) { 2774 void GetUniquePadsFromNpad(HLERequestContext& ctx) {
2764 IPC::RequestParser rp{ctx}; 2775 IPC::RequestParser rp{ctx};
2765 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; 2776 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2766 2777
2767 const s64 total_entries = 0;
2768 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); 2778 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2769 2779
2780 const std::vector<Core::HID::UniquePadId> unique_pads{};
2781
2782 ctx.WriteBuffer(unique_pads);
2783
2770 IPC::ResponseBuilder rb{ctx, 3}; 2784 IPC::ResponseBuilder rb{ctx, 3};
2771 rb.Push(ResultSuccess); 2785 rb.Push(ResultSuccess);
2772 rb.Push(total_entries); 2786 rb.Push(static_cast<u32>(unique_pads.size()));
2773 } 2787 }
2788
2789 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
2790 LOG_INFO(Service_AM, "called");
2791
2792 IPC::ResponseBuilder rb{ctx, 2, 1};
2793 rb.Push(ResultSuccess);
2794 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
2795 }
2796
2797 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
2798 const bool is_enabled = false;
2799
2800 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
2801
2802 IPC::ResponseBuilder rb{ctx, 3};
2803 rb.Push(ResultSuccess);
2804 rb.Push(is_enabled);
2805 }
2806
2807 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
2808 LOG_WARNING(Service_HID, "(STUBBED) called");
2809
2810 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
2811 .mode = Core::HID::TouchScreenModeForNx::Finger,
2812 };
2813
2814 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
2815 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
2816 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
2817 }
2818
2819 IPC::ResponseBuilder rb{ctx, 6};
2820 rb.Push(ResultSuccess);
2821 rb.PushRaw(touchscreen_config);
2822 }
2823
2824 Kernel::KEvent* joy_detach_event;
2825 KernelHelpers::ServiceContext service_context;
2774}; 2826};
2775 2827
2776void LoopProcess(Core::System& system) { 2828void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index b16f9933f..dc6917d5d 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -449,6 +449,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
449 case NativeWindowScalingMode::ScaleToWindow: 449 case NativeWindowScalingMode::ScaleToWindow:
450 case NativeWindowScalingMode::ScaleCrop: 450 case NativeWindowScalingMode::ScaleCrop:
451 case NativeWindowScalingMode::NoScaleCrop: 451 case NativeWindowScalingMode::NoScaleCrop:
452 case NativeWindowScalingMode::PreserveAspectRatio:
452 break; 453 break;
453 default: 454 default:
454 LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode); 455 LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode);
diff --git a/src/core/hle/service/nvnflinger/window.h b/src/core/hle/service/nvnflinger/window.h
index 61cca5b01..36d6cde3d 100644
--- a/src/core/hle/service/nvnflinger/window.h
+++ b/src/core/hle/service/nvnflinger/window.h
@@ -41,6 +41,7 @@ enum class NativeWindowScalingMode : s32 {
41 ScaleToWindow = 1, 41 ScaleToWindow = 1,
42 ScaleCrop = 2, 42 ScaleCrop = 2,
43 NoScaleCrop = 3, 43 NoScaleCrop = 3,
44 PreserveAspectRatio = 4,
44}; 45};
45 46
46/// Transform parameter for QueueBuffer 47/// Transform parameter for QueueBuffer
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 77426c46e..f86af01a4 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -18,7 +18,9 @@ enum class Errno : u32 {
18 AGAIN = 11, 18 AGAIN = 11,
19 INVAL = 22, 19 INVAL = 22,
20 MFILE = 24, 20 MFILE = 24,
21 PIPE = 32,
21 MSGSIZE = 90, 22 MSGSIZE = 90,
23 CONNABORTED = 103,
22 CONNRESET = 104, 24 CONNRESET = 104,
23 NOTCONN = 107, 25 NOTCONN = 107,
24 TIMEDOUT = 110, 26 TIMEDOUT = 110,
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index c1187209f..aed05250c 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -23,10 +23,14 @@ Errno Translate(Network::Errno value) {
23 return Errno::INVAL; 23 return Errno::INVAL;
24 case Network::Errno::MFILE: 24 case Network::Errno::MFILE:
25 return Errno::MFILE; 25 return Errno::MFILE;
26 case Network::Errno::PIPE:
27 return Errno::PIPE;
26 case Network::Errno::NOTCONN: 28 case Network::Errno::NOTCONN:
27 return Errno::NOTCONN; 29 return Errno::NOTCONN;
28 case Network::Errno::TIMEDOUT: 30 case Network::Errno::TIMEDOUT:
29 return Errno::TIMEDOUT; 31 return Errno::TIMEDOUT;
32 case Network::Errno::CONNABORTED:
33 return Errno::CONNABORTED;
30 case Network::Errno::CONNRESET: 34 case Network::Errno::CONNRESET:
31 return Errno::CONNRESET; 35 return Errno::CONNRESET;
32 case Network::Errno::INPROGRESS: 36 case Network::Errno::INPROGRESS:
diff --git a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
index 370678f48..c48914f64 100644
--- a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
+++ b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
@@ -100,7 +100,7 @@ public:
100 100
101 Result DoHandshake() override { 101 Result DoHandshake() override {
102 OSStatus status = SSLHandshake(context); 102 OSStatus status = SSLHandshake(context);
103 return HandleReturn("SSLHandshake", 0, status).Code(); 103 return HandleReturn("SSLHandshake", 0, status);
104 } 104 }
105 105
106 Result Read(size_t* out_size, std::span<u8> data) override { 106 Result Read(size_t* out_size, std::span<u8> data) override {
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 28f89c599..5d28300e6 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -39,6 +39,11 @@ namespace Network {
39 39
40namespace { 40namespace {
41 41
42enum class CallType {
43 Send,
44 Other,
45};
46
42#ifdef _WIN32 47#ifdef _WIN32
43 48
44using socklen_t = int; 49using socklen_t = int;
@@ -96,7 +101,7 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
96 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; 101 return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
97} 102}
98 103
99Errno TranslateNativeError(int e) { 104Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
100 switch (e) { 105 switch (e) {
101 case 0: 106 case 0:
102 return Errno::SUCCESS; 107 return Errno::SUCCESS;
@@ -112,6 +117,14 @@ Errno TranslateNativeError(int e) {
112 return Errno::AGAIN; 117 return Errno::AGAIN;
113 case WSAECONNREFUSED: 118 case WSAECONNREFUSED:
114 return Errno::CONNREFUSED; 119 return Errno::CONNREFUSED;
120 case WSAECONNABORTED:
121 if (call_type == CallType::Send) {
122 // Winsock yields WSAECONNABORTED from `send` in situations where Unix
123 // systems, and actual Switches, yield EPIPE.
124 return Errno::PIPE;
125 } else {
126 return Errno::CONNABORTED;
127 }
115 case WSAECONNRESET: 128 case WSAECONNRESET:
116 return Errno::CONNRESET; 129 return Errno::CONNRESET;
117 case WSAEHOSTUNREACH: 130 case WSAEHOSTUNREACH:
@@ -198,7 +211,7 @@ bool EnableNonBlock(int fd, bool enable) {
198 return fcntl(fd, F_SETFL, flags) == 0; 211 return fcntl(fd, F_SETFL, flags) == 0;
199} 212}
200 213
201Errno TranslateNativeError(int e) { 214Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
202 switch (e) { 215 switch (e) {
203 case 0: 216 case 0:
204 return Errno::SUCCESS; 217 return Errno::SUCCESS;
@@ -208,6 +221,10 @@ Errno TranslateNativeError(int e) {
208 return Errno::INVAL; 221 return Errno::INVAL;
209 case EMFILE: 222 case EMFILE:
210 return Errno::MFILE; 223 return Errno::MFILE;
224 case EPIPE:
225 return Errno::PIPE;
226 case ECONNABORTED:
227 return Errno::CONNABORTED;
211 case ENOTCONN: 228 case ENOTCONN:
212 return Errno::NOTCONN; 229 return Errno::NOTCONN;
213 case EAGAIN: 230 case EAGAIN:
@@ -236,13 +253,13 @@ Errno TranslateNativeError(int e) {
236 253
237#endif 254#endif
238 255
239Errno GetAndLogLastError() { 256Errno GetAndLogLastError(CallType call_type = CallType::Other) {
240#ifdef _WIN32 257#ifdef _WIN32
241 int e = WSAGetLastError(); 258 int e = WSAGetLastError();
242#else 259#else
243 int e = errno; 260 int e = errno;
244#endif 261#endif
245 const Errno err = TranslateNativeError(e); 262 const Errno err = TranslateNativeError(e, call_type);
246 if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) { 263 if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
247 // These happen during normal operation, so only log them at debug level. 264 // These happen during normal operation, so only log them at debug level.
248 LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); 265 LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
@@ -476,7 +493,13 @@ NetworkInstance::~NetworkInstance() {
476std::optional<IPv4Address> GetHostIPv4Address() { 493std::optional<IPv4Address> GetHostIPv4Address() {
477 const auto network_interface = Network::GetSelectedNetworkInterface(); 494 const auto network_interface = Network::GetSelectedNetworkInterface();
478 if (!network_interface.has_value()) { 495 if (!network_interface.has_value()) {
479 LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface"); 496 // Only print the error once to avoid log spam
497 static bool print_error = true;
498 if (print_error) {
499 LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
500 print_error = false;
501 }
502
480 return {}; 503 return {};
481 } 504 }
482 505
@@ -725,13 +748,17 @@ std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
725 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 748 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
726 ASSERT(flags == 0); 749 ASSERT(flags == 0);
727 750
751 int native_flags = 0;
752#if YUZU_UNIX
753 native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
754#endif
728 const auto result = send(fd, reinterpret_cast<const char*>(message.data()), 755 const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
729 static_cast<int>(message.size()), 0); 756 static_cast<int>(message.size()), native_flags);
730 if (result != SOCKET_ERROR) { 757 if (result != SOCKET_ERROR) {
731 return {static_cast<s32>(result), Errno::SUCCESS}; 758 return {static_cast<s32>(result), Errno::SUCCESS};
732 } 759 }
733 760
734 return {-1, GetAndLogLastError()}; 761 return {-1, GetAndLogLastError(CallType::Send)};
735} 762}
736 763
737std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message, 764std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
@@ -753,7 +780,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
753 return {static_cast<s32>(result), Errno::SUCCESS}; 780 return {static_cast<s32>(result), Errno::SUCCESS};
754 } 781 }
755 782
756 return {-1, GetAndLogLastError()}; 783 return {-1, GetAndLogLastError(CallType::Send)};
757} 784}
758 785
759Errno Socket::Close() { 786Errno Socket::Close() {
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index badcb8369..c7e20ae34 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -33,10 +33,12 @@ enum class Errno {
33 BADF, 33 BADF,
34 INVAL, 34 INVAL,
35 MFILE, 35 MFILE,
36 PIPE,
36 NOTCONN, 37 NOTCONN,
37 AGAIN, 38 AGAIN,
38 CONNREFUSED, 39 CONNREFUSED,
39 CONNRESET, 40 CONNRESET,
41 CONNABORTED,
40 HOSTUNREACH, 42 HOSTUNREACH,
41 NETDOWN, 43 NETDOWN,
42 NETUNREACH, 44 NETUNREACH,
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 4c909a6d3..7c37f660b 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -200,7 +200,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
200 }); 200 });
201 201
202 if (res == network_interfaces.end()) { 202 if (res == network_interfaces.end()) {
203 LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); 203 // Only print the error once to avoid log spam
204 static bool print_error = true;
205 if (print_error) {
206 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"",
207 selected_network_interface);
208 print_error = false;
209 }
210
204 return std::nullopt; 211 return std::nullopt;
205 } 212 }
206 213
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index f822fa856..44a771d65 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -220,7 +220,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
220 ASSERT(num_textures <= MAX_TEXTURES); 220 ASSERT(num_textures <= MAX_TEXTURES);
221 ASSERT(num_images <= MAX_IMAGES); 221 ASSERT(num_images <= MAX_IMAGES);
222 222
223 const bool assembly_shaders{assembly_programs[0].handle != 0}; 223 const auto backend = device.GetShaderBackend();
224 const bool assembly_shaders{backend == Settings::ShaderBackend::Glasm};
224 use_storage_buffers = 225 use_storage_buffers =
225 !assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); 226 !assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
226 writes_global_memory &= !use_storage_buffers; 227 writes_global_memory &= !use_storage_buffers;
@@ -230,7 +231,6 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
230 GenerateTransformFeedbackState(); 231 GenerateTransformFeedbackState();
231 } 232 }
232 const bool in_parallel = thread_worker != nullptr; 233 const bool in_parallel = thread_worker != nullptr;
233 const auto backend = device.GetShaderBackend();
234 auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv), 234 auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv),
235 shader_notify, backend, in_parallel, 235 shader_notify, backend, in_parallel,
236 force_context_flush](ShaderContext::Context*) mutable { 236 force_context_flush](ShaderContext::Context*) mutable {
@@ -559,15 +559,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
559} 559}
560 560
561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { 561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
562 glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides, 562 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS);
563 xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
564} 563}
565 564
566void GraphicsPipeline::GenerateTransformFeedbackState() { 565void GraphicsPipeline::GenerateTransformFeedbackState() {
567 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal 566 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
568 // when this is required. 567 // when this is required.
569 GLint* cursor{xfb_attribs.data()}; 568 GLint* cursor{xfb_attribs.data()};
570 GLint* current_stream{xfb_streams.data()};
571 569
572 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { 570 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
573 const auto& layout = key.xfb_state.layouts[feedback]; 571 const auto& layout = key.xfb_state.layouts[feedback];
@@ -575,15 +573,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
575 if (layout.varying_count == 0) { 573 if (layout.varying_count == 0) {
576 continue; 574 continue;
577 } 575 }
578 *current_stream = static_cast<GLint>(feedback);
579 if (current_stream != xfb_streams.data()) {
580 // When stepping one stream, push the expected token
581 cursor[0] = GL_NEXT_BUFFER_NV;
582 cursor[1] = 0;
583 cursor[2] = 0;
584 cursor += XFB_ENTRY_STRIDE;
585 }
586 ++current_stream;
587 576
588 const auto& locations = key.xfb_state.varyings[feedback]; 577 const auto& locations = key.xfb_state.varyings[feedback];
589 std::optional<u32> current_index; 578 std::optional<u32> current_index;
@@ -619,7 +608,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
619 } 608 }
620 } 609 }
621 num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE); 610 num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE);
622 num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
623} 611}
624 612
625void GraphicsPipeline::WaitForBuild() { 613void GraphicsPipeline::WaitForBuild() {
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 7b3d7eae8..74fc9cc3d 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -154,9 +154,7 @@ private:
154 154
155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
156 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
157 GLsizei num_xfb_strides{};
158 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; 157 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
159 std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
160 158
161 std::mutex built_mutex; 159 std::mutex built_mutex;
162 std::condition_variable built_condvar; 160 std::condition_variable built_condvar;
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index a8540339d..35bf80ea3 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -126,7 +126,7 @@ struct FormatTuple {
126 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM 126 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM
127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
129 {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM 129 {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable}, // A2R10G10B10_UNORM
130 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 130 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
131 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) 131 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
132 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 132 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 710929ac5..617417040 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -71,6 +71,11 @@ constexpr std::array R8G8B8_SSCALED{
71 VK_FORMAT_UNDEFINED, 71 VK_FORMAT_UNDEFINED,
72}; 72};
73 73
74constexpr std::array VK_FORMAT_R32G32B32_SFLOAT{
75 VK_FORMAT_R32G32B32A32_SFLOAT,
76 VK_FORMAT_UNDEFINED,
77};
78
74} // namespace Alternatives 79} // namespace Alternatives
75 80
76enum class NvidiaArchitecture { 81enum class NvidiaArchitecture {
@@ -103,6 +108,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
103 return Alternatives::R16G16B16_SSCALED.data(); 108 return Alternatives::R16G16B16_SSCALED.data();
104 case VK_FORMAT_R8G8B8_SSCALED: 109 case VK_FORMAT_R8G8B8_SSCALED:
105 return Alternatives::R8G8B8_SSCALED.data(); 110 return Alternatives::R8G8B8_SSCALED.data();
111 case VK_FORMAT_R32G32B32_SFLOAT:
112 return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
106 default: 113 default:
107 return nullptr; 114 return nullptr;
108 } 115 }
@@ -130,6 +137,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
130 VK_FORMAT_A2B10G10R10_UINT_PACK32, 137 VK_FORMAT_A2B10G10R10_UINT_PACK32,
131 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 138 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
132 VK_FORMAT_A2B10G10R10_USCALED_PACK32, 139 VK_FORMAT_A2B10G10R10_USCALED_PACK32,
140 VK_FORMAT_A2R10G10B10_UNORM_PACK32,
133 VK_FORMAT_A8B8G8R8_SINT_PACK32, 141 VK_FORMAT_A8B8G8R8_SINT_PACK32,
134 VK_FORMAT_A8B8G8R8_SNORM_PACK32, 142 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
135 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 143 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
@@ -326,6 +334,43 @@ std::vector<const char*> ExtensionListForVulkan(
326 334
327} // Anonymous namespace 335} // Anonymous namespace
328 336
337void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
338 extension = false;
339 loaded_extensions.erase(extension_name);
340}
341
342void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
343 if (loaded_extensions.contains(extension_name) && !is_suitable) {
344 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
345 this->RemoveExtension(is_suitable, extension_name);
346 }
347}
348
349template <typename Feature>
350void Device::RemoveExtensionFeature(bool& extension, Feature& feature,
351 const std::string& extension_name) {
352 // Unload extension.
353 this->RemoveExtension(extension, extension_name);
354
355 // Save sType and pNext for chain.
356 VkStructureType sType = feature.sType;
357 void* pNext = feature.pNext;
358
359 // Clear feature struct and restore chain.
360 feature = {};
361 feature.sType = sType;
362 feature.pNext = pNext;
363}
364
365template <typename Feature>
366void Device::RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
367 const std::string& extension_name) {
368 if (loaded_extensions.contains(extension_name) && !is_suitable) {
369 LOG_WARNING(Render_Vulkan, "Removing features for unsuitable extension {}", extension_name);
370 this->RemoveExtensionFeature(is_suitable, feature, extension_name);
371 }
372}
373
329Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 374Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
330 const vk::InstanceDispatch& dld_) 375 const vk::InstanceDispatch& dld_)
331 : instance{instance_}, dld{dld_}, physical{physical_}, 376 : instance{instance_}, dld{dld_}, physical{physical_},
@@ -397,21 +442,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
397 if (is_qualcomm || is_turnip) { 442 if (is_qualcomm || is_turnip) {
398 LOG_WARNING(Render_Vulkan, 443 LOG_WARNING(Render_Vulkan,
399 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); 444 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
400 extensions.custom_border_color = false; 445 RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
401 loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 446 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
402 } 447 }
403 448
404 if (is_qualcomm) { 449 if (is_qualcomm) {
405 must_emulate_scaled_formats = true; 450 must_emulate_scaled_formats = true;
406 451
407 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state"); 452 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
408 extensions.extended_dynamic_state = false; 453 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
409 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 454 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
410 455
411 LOG_WARNING(Render_Vulkan, 456 LOG_WARNING(Render_Vulkan,
412 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); 457 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
413 extensions.push_descriptor = false; 458 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
414 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
415 459
416#if defined(ANDROID) && defined(ARCHITECTURE_arm64) 460#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
417 // Patch the driver to enable BCn textures. 461 // Patch the driver to enable BCn textures.
@@ -440,15 +484,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
440 must_emulate_scaled_formats = true; 484 must_emulate_scaled_formats = true;
441 485
442 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); 486 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
443 extensions.extended_dynamic_state = false; 487 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
444 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 488 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
445 489
446 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); 490 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
447 features.extended_dynamic_state2.extendedDynamicState2 = false; 491 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
448 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 492 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
449 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
450 extensions.extended_dynamic_state2 = false;
451 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
452 } 493 }
453 494
454 if (is_nvidia) { 495 if (is_nvidia) {
@@ -464,8 +505,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
464 case NvidiaArchitecture::VoltaOrOlder: 505 case NvidiaArchitecture::VoltaOrOlder:
465 if (nv_major_version < 527) { 506 if (nv_major_version < 527) {
466 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); 507 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
467 extensions.push_descriptor = false; 508 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
468 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
469 } 509 }
470 break; 510 break;
471 } 511 }
@@ -480,8 +520,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
480 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { 520 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
481 LOG_WARNING(Render_Vulkan, 521 LOG_WARNING(Render_Vulkan,
482 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); 522 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
483 extensions.extended_dynamic_state = false; 523 RemoveExtensionFeature(extensions.extended_dynamic_state,
484 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 524 features.extended_dynamic_state,
525 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
485 } 526 }
486 } 527 }
487 if (extensions.extended_dynamic_state2 && is_radv) { 528 if (extensions.extended_dynamic_state2 && is_radv) {
@@ -490,11 +531,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
490 LOG_WARNING( 531 LOG_WARNING(
491 Render_Vulkan, 532 Render_Vulkan,
492 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); 533 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
493 features.extended_dynamic_state2.extendedDynamicState2 = false; 534 RemoveExtensionFeature(extensions.extended_dynamic_state2,
494 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 535 features.extended_dynamic_state2,
495 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 536 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
496 extensions.extended_dynamic_state2 = false;
497 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
498 } 537 }
499 } 538 }
500 if (extensions.extended_dynamic_state2 && is_qualcomm) { 539 if (extensions.extended_dynamic_state2 && is_qualcomm) {
@@ -504,11 +543,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
504 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. 543 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
505 LOG_WARNING(Render_Vulkan, 544 LOG_WARNING(Render_Vulkan,
506 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); 545 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
507 features.extended_dynamic_state2.extendedDynamicState2 = false; 546 RemoveExtensionFeature(extensions.extended_dynamic_state2,
508 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 547 features.extended_dynamic_state2,
509 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 548 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
510 extensions.extended_dynamic_state2 = false;
511 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
512 } 549 }
513 } 550 }
514 if (extensions.extended_dynamic_state3 && is_radv) { 551 if (extensions.extended_dynamic_state3 && is_radv) {
@@ -540,9 +577,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
540 if (is_rdna2) { 577 if (is_rdna2) {
541 LOG_WARNING(Render_Vulkan, 578 LOG_WARNING(Render_Vulkan,
542 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); 579 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
543 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 580 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
544 extensions.vertex_input_dynamic_state = false; 581 features.vertex_input_dynamic_state,
545 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 582 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
546 } 583 }
547 } 584 }
548 if (extensions.vertex_input_dynamic_state && is_qualcomm) { 585 if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@@ -553,9 +590,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
553 LOG_WARNING( 590 LOG_WARNING(
554 Render_Vulkan, 591 Render_Vulkan,
555 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); 592 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
556 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 593 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
557 extensions.vertex_input_dynamic_state = false; 594 features.vertex_input_dynamic_state,
558 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 595 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
559 } 596 }
560 } 597 }
561 598
@@ -575,8 +612,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
575 if (!features.shader_float16_int8.shaderFloat16) { 612 if (!features.shader_float16_int8.shaderFloat16) {
576 LOG_WARNING(Render_Vulkan, 613 LOG_WARNING(Render_Vulkan,
577 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); 614 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
578 extensions.sampler_filter_minmax = false; 615 RemoveExtension(extensions.sampler_filter_minmax,
579 loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); 616 VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
580 } 617 }
581 } 618 }
582 619
@@ -584,8 +621,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
584 const u32 version = (properties.properties.driverVersion << 3) >> 3; 621 const u32 version = (properties.properties.driverVersion << 3) >> 3;
585 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { 622 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
586 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); 623 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
587 extensions.vertex_input_dynamic_state = false; 624 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
588 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 625 features.vertex_input_dynamic_state,
626 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
589 } 627 }
590 } 628 }
591 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { 629 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@@ -612,8 +650,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
612 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc 650 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
613 LOG_WARNING(Render_Vulkan, 651 LOG_WARNING(Render_Vulkan,
614 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); 652 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
615 extensions.push_descriptor = false; 653 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
616 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
617 } 654 }
618 } 655 }
619 if (is_mvk) { 656 if (is_mvk) {
@@ -1007,34 +1044,29 @@ bool Device::GetSuitability(bool requires_swapchain) {
1007 return suitable; 1044 return suitable;
1008} 1045}
1009 1046
1010void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
1011 if (loaded_extensions.contains(extension_name) && !is_suitable) {
1012 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
1013 loaded_extensions.erase(extension_name);
1014 }
1015}
1016
1017void Device::RemoveUnsuitableExtensions() { 1047void Device::RemoveUnsuitableExtensions() {
1018 // VK_EXT_custom_border_color 1048 // VK_EXT_custom_border_color
1019 extensions.custom_border_color = features.custom_border_color.customBorderColors && 1049 extensions.custom_border_color = features.custom_border_color.customBorderColors &&
1020 features.custom_border_color.customBorderColorWithoutFormat; 1050 features.custom_border_color.customBorderColorWithoutFormat;
1021 RemoveExtensionIfUnsuitable(extensions.custom_border_color, 1051 RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
1022 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 1052 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
1023 1053
1024 // VK_EXT_depth_clip_control 1054 // VK_EXT_depth_clip_control
1025 extensions.depth_clip_control = features.depth_clip_control.depthClipControl; 1055 extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
1026 RemoveExtensionIfUnsuitable(extensions.depth_clip_control, 1056 RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
1027 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); 1057 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
1028 1058
1029 // VK_EXT_extended_dynamic_state 1059 // VK_EXT_extended_dynamic_state
1030 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; 1060 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
1031 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, 1061 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
1032 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 1062 features.extended_dynamic_state,
1063 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
1033 1064
1034 // VK_EXT_extended_dynamic_state2 1065 // VK_EXT_extended_dynamic_state2
1035 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; 1066 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
1036 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, 1067 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
1037 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); 1068 features.extended_dynamic_state2,
1069 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
1038 1070
1039 // VK_EXT_extended_dynamic_state3 1071 // VK_EXT_extended_dynamic_state3
1040 dynamic_state3_blending = 1072 dynamic_state3_blending =
@@ -1048,35 +1080,38 @@ void Device::RemoveUnsuitableExtensions() {
1048 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; 1080 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
1049 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; 1081 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
1050 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; 1082 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
1051 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, 1083 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
1052 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); 1084 features.extended_dynamic_state3,
1085 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
1053 1086
1054 // VK_EXT_provoking_vertex 1087 // VK_EXT_provoking_vertex
1055 extensions.provoking_vertex = 1088 extensions.provoking_vertex =
1056 features.provoking_vertex.provokingVertexLast && 1089 features.provoking_vertex.provokingVertexLast &&
1057 features.provoking_vertex.transformFeedbackPreservesProvokingVertex; 1090 features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
1058 RemoveExtensionIfUnsuitable(extensions.provoking_vertex, 1091 RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
1059 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); 1092 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
1060 1093
1061 // VK_KHR_shader_atomic_int64 1094 // VK_KHR_shader_atomic_int64
1062 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && 1095 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
1063 features.shader_atomic_int64.shaderSharedInt64Atomics; 1096 features.shader_atomic_int64.shaderSharedInt64Atomics;
1064 RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, 1097 RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
1065 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); 1098 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
1066 1099
1067 // VK_EXT_shader_demote_to_helper_invocation 1100 // VK_EXT_shader_demote_to_helper_invocation
1068 extensions.shader_demote_to_helper_invocation = 1101 extensions.shader_demote_to_helper_invocation =
1069 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; 1102 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
1070 RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, 1103 RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
1071 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); 1104 features.shader_demote_to_helper_invocation,
1105 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
1072 1106
1073 // VK_EXT_subgroup_size_control 1107 // VK_EXT_subgroup_size_control
1074 extensions.subgroup_size_control = 1108 extensions.subgroup_size_control =
1075 features.subgroup_size_control.subgroupSizeControl && 1109 features.subgroup_size_control.subgroupSizeControl &&
1076 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && 1110 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
1077 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; 1111 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
1078 RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, 1112 RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
1079 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); 1113 features.subgroup_size_control,
1114 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
1080 1115
1081 // VK_EXT_transform_feedback 1116 // VK_EXT_transform_feedback
1082 extensions.transform_feedback = 1117 extensions.transform_feedback =
@@ -1086,24 +1121,27 @@ void Device::RemoveUnsuitableExtensions() {
1086 properties.transform_feedback.maxTransformFeedbackBuffers > 0 && 1121 properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
1087 properties.transform_feedback.transformFeedbackQueries && 1122 properties.transform_feedback.transformFeedbackQueries &&
1088 properties.transform_feedback.transformFeedbackDraw; 1123 properties.transform_feedback.transformFeedbackDraw;
1089 RemoveExtensionIfUnsuitable(extensions.transform_feedback, 1124 RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
1090 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); 1125 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
1091 1126
1092 // VK_EXT_vertex_input_dynamic_state 1127 // VK_EXT_vertex_input_dynamic_state
1093 extensions.vertex_input_dynamic_state = 1128 extensions.vertex_input_dynamic_state =
1094 features.vertex_input_dynamic_state.vertexInputDynamicState; 1129 features.vertex_input_dynamic_state.vertexInputDynamicState;
1095 RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, 1130 RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
1096 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 1131 features.vertex_input_dynamic_state,
1132 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
1097 1133
1098 // VK_KHR_pipeline_executable_properties 1134 // VK_KHR_pipeline_executable_properties
1099 if (Settings::values.renderer_shader_feedback.GetValue()) { 1135 if (Settings::values.renderer_shader_feedback.GetValue()) {
1100 extensions.pipeline_executable_properties = 1136 extensions.pipeline_executable_properties =
1101 features.pipeline_executable_properties.pipelineExecutableInfo; 1137 features.pipeline_executable_properties.pipelineExecutableInfo;
1102 RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, 1138 RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
1103 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1139 features.pipeline_executable_properties,
1140 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1104 } else { 1141 } else {
1105 extensions.pipeline_executable_properties = false; 1142 RemoveExtensionFeature(extensions.pipeline_executable_properties,
1106 loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1143 features.pipeline_executable_properties,
1144 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1107 } 1145 }
1108 1146
1109 // VK_KHR_workgroup_memory_explicit_layout 1147 // VK_KHR_workgroup_memory_explicit_layout
@@ -1113,8 +1151,9 @@ void Device::RemoveUnsuitableExtensions() {
1113 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && 1151 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
1114 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && 1152 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
1115 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; 1153 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
1116 RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, 1154 RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
1117 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); 1155 features.workgroup_memory_explicit_layout,
1156 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
1118} 1157}
1119 1158
1120void Device::SetupFamilies(VkSurfaceKHR surface) { 1159void Device::SetupFamilies(VkSurfaceKHR surface) {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d8dd41e51..488fdd313 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -639,8 +639,17 @@ private:
639 639
640 // Remove extensions which have incomplete feature support. 640 // Remove extensions which have incomplete feature support.
641 void RemoveUnsuitableExtensions(); 641 void RemoveUnsuitableExtensions();
642
643 void RemoveExtension(bool& extension, const std::string& extension_name);
642 void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name); 644 void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
643 645
646 template <typename Feature>
647 void RemoveExtensionFeature(bool& extension, Feature& feature,
648 const std::string& extension_name);
649 template <typename Feature>
650 void RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
651 const std::string& extension_name);
652
644 /// Sets up queue families. 653 /// Sets up queue families.
645 void SetupFamilies(VkSurfaceKHR surface); 654 void SetupFamilies(VkSurfaceKHR surface);
646 655