summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/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/res/layout/dialog_slider.xml13
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml62
-rw-r--r--src/android/app/src/main/res/values/strings.xml1
-rw-r--r--src/core/hid/hid_types.h26
-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/ssl/ssl_backend_securetransport.cpp2
-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/vulkan_common/vulkan_device.cpp179
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h9
22 files changed, 312 insertions, 175 deletions
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/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/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/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/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 540ea5ef4..de1b2909b 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -150,6 +150,7 @@
150 <string name="frame_limit_slider">Limit speed percent</string> 150 <string name="frame_limit_slider">Limit speed percent</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="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>
152 <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>
153 154
154 <!-- System settings strings --> 155 <!-- System settings strings -->
155 <string name="use_docked_mode">Docked Mode</string> 156 <string name="use_docked_mode">Docked Mode</string>
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/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/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/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/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 710929ac5..adde96aa5 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -326,6 +326,43 @@ std::vector<const char*> ExtensionListForVulkan(
326 326
327} // Anonymous namespace 327} // Anonymous namespace
328 328
329void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
330 extension = false;
331 loaded_extensions.erase(extension_name);
332}
333
334void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
335 if (loaded_extensions.contains(extension_name) && !is_suitable) {
336 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
337 this->RemoveExtension(is_suitable, extension_name);
338 }
339}
340
341template <typename Feature>
342void Device::RemoveExtensionFeature(bool& extension, Feature& feature,
343 const std::string& extension_name) {
344 // Unload extension.
345 this->RemoveExtension(extension, extension_name);
346
347 // Save sType and pNext for chain.
348 VkStructureType sType = feature.sType;
349 void* pNext = feature.pNext;
350
351 // Clear feature struct and restore chain.
352 feature = {};
353 feature.sType = sType;
354 feature.pNext = pNext;
355}
356
357template <typename Feature>
358void Device::RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
359 const std::string& extension_name) {
360 if (loaded_extensions.contains(extension_name) && !is_suitable) {
361 LOG_WARNING(Render_Vulkan, "Removing features for unsuitable extension {}", extension_name);
362 this->RemoveExtensionFeature(is_suitable, feature, extension_name);
363 }
364}
365
329Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 366Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
330 const vk::InstanceDispatch& dld_) 367 const vk::InstanceDispatch& dld_)
331 : instance{instance_}, dld{dld_}, physical{physical_}, 368 : instance{instance_}, dld{dld_}, physical{physical_},
@@ -397,21 +434,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
397 if (is_qualcomm || is_turnip) { 434 if (is_qualcomm || is_turnip) {
398 LOG_WARNING(Render_Vulkan, 435 LOG_WARNING(Render_Vulkan,
399 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); 436 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
400 extensions.custom_border_color = false; 437 RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
401 loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 438 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
402 } 439 }
403 440
404 if (is_qualcomm) { 441 if (is_qualcomm) {
405 must_emulate_scaled_formats = true; 442 must_emulate_scaled_formats = true;
406 443
407 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state"); 444 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
408 extensions.extended_dynamic_state = false; 445 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
409 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 446 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
410 447
411 LOG_WARNING(Render_Vulkan, 448 LOG_WARNING(Render_Vulkan,
412 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); 449 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
413 extensions.push_descriptor = false; 450 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
414 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
415 451
416#if defined(ANDROID) && defined(ARCHITECTURE_arm64) 452#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
417 // Patch the driver to enable BCn textures. 453 // Patch the driver to enable BCn textures.
@@ -440,15 +476,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
440 must_emulate_scaled_formats = true; 476 must_emulate_scaled_formats = true;
441 477
442 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); 478 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
443 extensions.extended_dynamic_state = false; 479 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
444 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 480 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
445 481
446 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); 482 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
447 features.extended_dynamic_state2.extendedDynamicState2 = false; 483 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
448 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 484 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 } 485 }
453 486
454 if (is_nvidia) { 487 if (is_nvidia) {
@@ -464,8 +497,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
464 case NvidiaArchitecture::VoltaOrOlder: 497 case NvidiaArchitecture::VoltaOrOlder:
465 if (nv_major_version < 527) { 498 if (nv_major_version < 527) {
466 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); 499 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
467 extensions.push_descriptor = false; 500 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
468 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
469 } 501 }
470 break; 502 break;
471 } 503 }
@@ -480,8 +512,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
480 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { 512 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
481 LOG_WARNING(Render_Vulkan, 513 LOG_WARNING(Render_Vulkan,
482 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); 514 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
483 extensions.extended_dynamic_state = false; 515 RemoveExtensionFeature(extensions.extended_dynamic_state,
484 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 516 features.extended_dynamic_state,
517 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
485 } 518 }
486 } 519 }
487 if (extensions.extended_dynamic_state2 && is_radv) { 520 if (extensions.extended_dynamic_state2 && is_radv) {
@@ -490,11 +523,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
490 LOG_WARNING( 523 LOG_WARNING(
491 Render_Vulkan, 524 Render_Vulkan,
492 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); 525 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
493 features.extended_dynamic_state2.extendedDynamicState2 = false; 526 RemoveExtensionFeature(extensions.extended_dynamic_state2,
494 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 527 features.extended_dynamic_state2,
495 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 528 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 } 529 }
499 } 530 }
500 if (extensions.extended_dynamic_state2 && is_qualcomm) { 531 if (extensions.extended_dynamic_state2 && is_qualcomm) {
@@ -504,11 +535,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
504 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. 535 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
505 LOG_WARNING(Render_Vulkan, 536 LOG_WARNING(Render_Vulkan,
506 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); 537 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
507 features.extended_dynamic_state2.extendedDynamicState2 = false; 538 RemoveExtensionFeature(extensions.extended_dynamic_state2,
508 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 539 features.extended_dynamic_state2,
509 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 540 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 } 541 }
513 } 542 }
514 if (extensions.extended_dynamic_state3 && is_radv) { 543 if (extensions.extended_dynamic_state3 && is_radv) {
@@ -540,9 +569,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
540 if (is_rdna2) { 569 if (is_rdna2) {
541 LOG_WARNING(Render_Vulkan, 570 LOG_WARNING(Render_Vulkan,
542 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); 571 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
543 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 572 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
544 extensions.vertex_input_dynamic_state = false; 573 features.vertex_input_dynamic_state,
545 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 574 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
546 } 575 }
547 } 576 }
548 if (extensions.vertex_input_dynamic_state && is_qualcomm) { 577 if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@@ -553,9 +582,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
553 LOG_WARNING( 582 LOG_WARNING(
554 Render_Vulkan, 583 Render_Vulkan,
555 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); 584 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
556 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 585 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
557 extensions.vertex_input_dynamic_state = false; 586 features.vertex_input_dynamic_state,
558 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 587 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
559 } 588 }
560 } 589 }
561 590
@@ -575,8 +604,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
575 if (!features.shader_float16_int8.shaderFloat16) { 604 if (!features.shader_float16_int8.shaderFloat16) {
576 LOG_WARNING(Render_Vulkan, 605 LOG_WARNING(Render_Vulkan,
577 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); 606 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
578 extensions.sampler_filter_minmax = false; 607 RemoveExtension(extensions.sampler_filter_minmax,
579 loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); 608 VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
580 } 609 }
581 } 610 }
582 611
@@ -584,8 +613,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
584 const u32 version = (properties.properties.driverVersion << 3) >> 3; 613 const u32 version = (properties.properties.driverVersion << 3) >> 3;
585 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { 614 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
586 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); 615 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
587 extensions.vertex_input_dynamic_state = false; 616 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
588 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 617 features.vertex_input_dynamic_state,
618 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
589 } 619 }
590 } 620 }
591 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { 621 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@@ -612,8 +642,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
612 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc 642 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
613 LOG_WARNING(Render_Vulkan, 643 LOG_WARNING(Render_Vulkan,
614 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); 644 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
615 extensions.push_descriptor = false; 645 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
616 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
617 } 646 }
618 } 647 }
619 if (is_mvk) { 648 if (is_mvk) {
@@ -1007,34 +1036,29 @@ bool Device::GetSuitability(bool requires_swapchain) {
1007 return suitable; 1036 return suitable;
1008} 1037}
1009 1038
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() { 1039void Device::RemoveUnsuitableExtensions() {
1018 // VK_EXT_custom_border_color 1040 // VK_EXT_custom_border_color
1019 extensions.custom_border_color = features.custom_border_color.customBorderColors && 1041 extensions.custom_border_color = features.custom_border_color.customBorderColors &&
1020 features.custom_border_color.customBorderColorWithoutFormat; 1042 features.custom_border_color.customBorderColorWithoutFormat;
1021 RemoveExtensionIfUnsuitable(extensions.custom_border_color, 1043 RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
1022 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 1044 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
1023 1045
1024 // VK_EXT_depth_clip_control 1046 // VK_EXT_depth_clip_control
1025 extensions.depth_clip_control = features.depth_clip_control.depthClipControl; 1047 extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
1026 RemoveExtensionIfUnsuitable(extensions.depth_clip_control, 1048 RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
1027 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); 1049 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
1028 1050
1029 // VK_EXT_extended_dynamic_state 1051 // VK_EXT_extended_dynamic_state
1030 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; 1052 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
1031 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, 1053 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
1032 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 1054 features.extended_dynamic_state,
1055 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
1033 1056
1034 // VK_EXT_extended_dynamic_state2 1057 // VK_EXT_extended_dynamic_state2
1035 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; 1058 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
1036 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, 1059 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
1037 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); 1060 features.extended_dynamic_state2,
1061 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
1038 1062
1039 // VK_EXT_extended_dynamic_state3 1063 // VK_EXT_extended_dynamic_state3
1040 dynamic_state3_blending = 1064 dynamic_state3_blending =
@@ -1048,35 +1072,38 @@ void Device::RemoveUnsuitableExtensions() {
1048 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; 1072 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
1049 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; 1073 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
1050 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; 1074 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
1051 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, 1075 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
1052 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); 1076 features.extended_dynamic_state3,
1077 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
1053 1078
1054 // VK_EXT_provoking_vertex 1079 // VK_EXT_provoking_vertex
1055 extensions.provoking_vertex = 1080 extensions.provoking_vertex =
1056 features.provoking_vertex.provokingVertexLast && 1081 features.provoking_vertex.provokingVertexLast &&
1057 features.provoking_vertex.transformFeedbackPreservesProvokingVertex; 1082 features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
1058 RemoveExtensionIfUnsuitable(extensions.provoking_vertex, 1083 RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
1059 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); 1084 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
1060 1085
1061 // VK_KHR_shader_atomic_int64 1086 // VK_KHR_shader_atomic_int64
1062 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && 1087 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
1063 features.shader_atomic_int64.shaderSharedInt64Atomics; 1088 features.shader_atomic_int64.shaderSharedInt64Atomics;
1064 RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, 1089 RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
1065 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); 1090 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
1066 1091
1067 // VK_EXT_shader_demote_to_helper_invocation 1092 // VK_EXT_shader_demote_to_helper_invocation
1068 extensions.shader_demote_to_helper_invocation = 1093 extensions.shader_demote_to_helper_invocation =
1069 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; 1094 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
1070 RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, 1095 RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
1071 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); 1096 features.shader_demote_to_helper_invocation,
1097 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
1072 1098
1073 // VK_EXT_subgroup_size_control 1099 // VK_EXT_subgroup_size_control
1074 extensions.subgroup_size_control = 1100 extensions.subgroup_size_control =
1075 features.subgroup_size_control.subgroupSizeControl && 1101 features.subgroup_size_control.subgroupSizeControl &&
1076 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && 1102 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
1077 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; 1103 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
1078 RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, 1104 RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
1079 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); 1105 features.subgroup_size_control,
1106 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
1080 1107
1081 // VK_EXT_transform_feedback 1108 // VK_EXT_transform_feedback
1082 extensions.transform_feedback = 1109 extensions.transform_feedback =
@@ -1086,24 +1113,27 @@ void Device::RemoveUnsuitableExtensions() {
1086 properties.transform_feedback.maxTransformFeedbackBuffers > 0 && 1113 properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
1087 properties.transform_feedback.transformFeedbackQueries && 1114 properties.transform_feedback.transformFeedbackQueries &&
1088 properties.transform_feedback.transformFeedbackDraw; 1115 properties.transform_feedback.transformFeedbackDraw;
1089 RemoveExtensionIfUnsuitable(extensions.transform_feedback, 1116 RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
1090 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); 1117 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
1091 1118
1092 // VK_EXT_vertex_input_dynamic_state 1119 // VK_EXT_vertex_input_dynamic_state
1093 extensions.vertex_input_dynamic_state = 1120 extensions.vertex_input_dynamic_state =
1094 features.vertex_input_dynamic_state.vertexInputDynamicState; 1121 features.vertex_input_dynamic_state.vertexInputDynamicState;
1095 RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, 1122 RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
1096 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 1123 features.vertex_input_dynamic_state,
1124 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
1097 1125
1098 // VK_KHR_pipeline_executable_properties 1126 // VK_KHR_pipeline_executable_properties
1099 if (Settings::values.renderer_shader_feedback.GetValue()) { 1127 if (Settings::values.renderer_shader_feedback.GetValue()) {
1100 extensions.pipeline_executable_properties = 1128 extensions.pipeline_executable_properties =
1101 features.pipeline_executable_properties.pipelineExecutableInfo; 1129 features.pipeline_executable_properties.pipelineExecutableInfo;
1102 RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, 1130 RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
1103 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1131 features.pipeline_executable_properties,
1132 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1104 } else { 1133 } else {
1105 extensions.pipeline_executable_properties = false; 1134 RemoveExtensionFeature(extensions.pipeline_executable_properties,
1106 loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1135 features.pipeline_executable_properties,
1136 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1107 } 1137 }
1108 1138
1109 // VK_KHR_workgroup_memory_explicit_layout 1139 // VK_KHR_workgroup_memory_explicit_layout
@@ -1113,8 +1143,9 @@ void Device::RemoveUnsuitableExtensions() {
1113 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && 1143 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
1114 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && 1144 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
1115 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; 1145 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
1116 RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, 1146 RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
1117 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); 1147 features.workgroup_memory_explicit_layout,
1148 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
1118} 1149}
1119 1150
1120void Device::SetupFamilies(VkSurfaceKHR surface) { 1151void 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