Compare commits

...

42 Commits

Author SHA1 Message Date
yuzubot
caa5dd442b Android #45 2023-08-20 00:57:19 +00:00
Fernando S
6a5db5679b Merge pull request #11320 from Kelebek1/mask_depthstencil_clear
Support masked depthstencil clears
2023-08-19 17:28:28 +02:00
Kelebek1
f2f99a8c31 Masked depthstencil clears 2023-08-19 03:29:46 +01:00
liamwhite
ae1421265a Merge pull request #11278 from Kelebek1/dma_sync
Mark accelerated DMA destination buffers and images as GPU-modified
2023-08-18 09:12:27 -04:00
liamwhite
314d3858a1 Merge pull request #11288 from liamwhite/svc-tick
kernel: remove relative task registration
2023-08-18 09:12:19 -04:00
liamwhite
0383ae1dbf Merge pull request #11310 from vonchenplus/vulkan_format
video_core: Fix vulkan format assert error
2023-08-18 09:12:11 -04:00
Feng Chen
1dcb0c2232 video_core: Fix vulkan assert error 2023-08-18 14:40:11 +08:00
liamwhite
ddedaa8875 Merge pull request #10989 from comex/epipe
sockets: Improve behavior when sending to closed connection
2023-08-17 11:59:47 -04:00
liamwhite
0e3a995bf4 cmake: mark warning disable for gcc 11 (#11301) 2023-08-17 16:03:34 +02:00
comex
755bcc459b Improve behavior when sending to closed connection
- On Unix, this would previously kill the Yuzu process with SIGPIPE.
  Send MSG_NOSIGNAL to opt out of this.

- Add support for the proper error code in this situation, EPIPE.

- Windows has nonstandard behavior in this situation; translate it to
  the standard behavior.  Kind of pointless, but isn't it nice to be
  correct?
2023-08-15 20:59:57 -04:00
Fernando S
a8c4f01f6c Merge pull request #11287 from liamwhite/replaced-bytes
gdbstub: fixup replaced instruction bytes in memory reads
2023-08-15 15:36:14 +02:00
bunnei
6d665a94ea Merge pull request #11256 from FearlessTobi/revert-10075
Partially Revert "Silence nifm spam"
2023-08-14 16:28:13 -07:00
bunnei
bbc6b08fc7 Merge pull request #11273 from t895/setup-completion
android: Setup additions
2023-08-14 15:41:35 -07:00
Liam
0bd9a4456c kernel: remove relative task registration 2023-08-14 18:12:06 -04:00
Liam
fbda084acb gdbstub: fixup replaced instruction bytes in memory reads 2023-08-14 16:33:27 -04:00
FearlessTobi
2694f81462 Revert "Silence nifm spam"
This reverts commit 4da4ecb1ff.
2023-08-14 21:23:09 +02:00
bunnei
d5adaeafdf Merge pull request #11271 from t895/settings-tweaks
android: Settings tweaks
2023-08-14 11:44:38 -07:00
liamwhite
58a4c86797 Merge pull request #11282 from ameerj/glasm-xfb
gl_graphics_pipeline: GLASM: Fix transform feedback with multiple buffers
2023-08-14 09:19:20 -04:00
liamwhite
35a77c3bb2 Merge pull request #11283 from ameerj/glasm-pipeline-detection
gl_graphics_pipeline: Fix GLASM storage buffer detection
2023-08-14 09:19:10 -04:00
liamwhite
c1016b68ae Merge pull request #11281 from liamwhite/vi-scale-mode
nvnflinger: add missing scale mode
2023-08-14 09:19:03 -04:00
liamwhite
b30df50076 Merge pull request #11259 from german77/hid
service: hid: Implement functions needed by QLaunch
2023-08-14 09:18:55 -04:00
liamwhite
5afe1367ba Merge pull request #11263 from liamwhite/my-feature-branch
vulkan_device: disable features associated with unloaded extensions
2023-08-14 09:18:47 -04:00
liamwhite
24700af3c2 Merge pull request #11264 from liamwhite/stray-code
ssl_backend_securetransport: remove stray .Code()
2023-08-14 09:18:32 -04:00
Ameer J
f9ef721ca6 gl_graphics_pipeline: Fix GLASM storage buffer detection 2023-08-13 17:06:45 -04:00
Ameer J
c34ed4bbd8 gl_graphics_pipeline: GLASM: Fix transform feedback with multiple buffers 2023-08-13 16:50:01 -04:00
Liam
7351884588 nvnflinger: add missing scale mode 2023-08-13 13:57:02 -04:00
Kelebek1
5a37b8f2c1 Mark accelerted DMA destination buffers and images as GPU-modified 2023-08-13 02:22:39 +01:00
Charles Lombardo
242ce2a0b3 android: Page forward on setup step completion 2023-08-12 20:21:47 -04:00
Charles Lombardo
8ab3685a39 android: Adjust setup fragment layout
Fixes padding issues in small and large layouts and allows viewpager to reach into system insets.
2023-08-12 17:02:59 -04:00
Charles Lombardo
8bd0521b58 android: Show complete indicator during setup 2023-08-12 16:53:14 -04:00
Charles Lombardo
64ea5522d3 android: Remove redundant option from slider dialog
You can already reset any setting by long pressing the settings item.
2023-08-12 15:45:27 -04:00
Charles Lombardo
798a439eb1 android: Reduce opacity of non-editable settings 2023-08-12 15:42:55 -04:00
Charles Lombardo
786b609151 android: Use string resource for slider value/units 2023-08-12 15:42:54 -04:00
Charles Lombardo
89a2d308c3 android: Display setting value in setting list items 2023-08-12 14:38:46 -04:00
Charles Lombardo
0d4bf53ad9 android: Set switch listener before assigning new value
Previously the switch could have its old listener triggered when recycled.
2023-08-12 01:00:42 -04:00
Liam
8b98c4e5a0 ssl_backend_securetransport: remove stray .Code() 2023-08-11 23:32:46 -04:00
liamwhite
26ff214719 Merge pull request #11219 from zeltermann/title-id-search
Allow searching by a substring of the title ID
2023-08-11 16:53:27 -04:00
liamwhite
640f7cd945 Merge pull request #11253 from liamwhite/i-hate-this-toolchain
general: fix apple clang build
2023-08-11 16:53:20 -04:00
Liam
7d8f748696 vulkan_device: disable features associated with unloaded extensions 2023-08-11 14:54:12 -04:00
Narr the Reg
bdd96118d1 service: hid: Implement functions needed by QLaunch 2023-08-11 10:13:21 -06:00
zeltermann
1ed9e8812b Allow searching by a substring of the title ID 2023-08-11 00:07:12 +07:00
Liam
023b9b38cc general: fix apple clang build 2023-08-09 22:38:37 -04:00
65 changed files with 1078 additions and 501 deletions

View File

@@ -1,3 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@@ -134,7 +134,7 @@ else()
endif() endif()
# GCC bugs # GCC bugs
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# These diagnostics would be great if they worked, but are just completely broken # These diagnostics would be great if they worked, but are just completely broken
# and produce bogus errors on external libraries like fmt. # and produce bogus errors on external libraries like fmt.
add_compile_options( add_compile_options(

View File

@@ -49,6 +49,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List
val context = YuzuApplication.appContext val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId) binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId) binding.textSettingDescription.text = context.getString(license.descriptionId)
binding.textSettingValue.visibility = View.GONE
} }
} }
} }

View File

@@ -5,13 +5,19 @@ package org.yuzu.yuzu_emu.adapters
import android.text.Html import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
@@ -26,7 +32,7 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
holder.bind(pages[position]) holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) : inner class SetupPageViewHolder(val binding: PageSetupBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root), SetupCallback {
lateinit var page: SetupPage lateinit var page: SetupPage
init { init {
@@ -35,6 +41,12 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
fun bind(page: SetupPage) { fun bind(page: SetupPage) {
this.page = page this.page = page
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE
}
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
@@ -62,9 +74,15 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
MaterialButton.ICON_GRAVITY_END MaterialButton.ICON_GRAVITY_END
} }
setOnClickListener { setOnClickListener {
page.buttonAction.invoke() page.buttonAction.invoke(this@SetupPageViewHolder)
} }
} }
} }
override fun onStepCompleted() {
ViewUtils.hideView(binding.buttonAction, 200)
ViewUtils.showView(binding.textConfirmation, 200)
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
}
} }
} }

View File

@@ -207,8 +207,11 @@ class SettingsAdapter(
val sliderBinding = DialogSliderBinding.inflate(inflater) val sliderBinding = DialogSliderBinding.inflate(inflater)
textSliderValue = sliderBinding.textValue textSliderValue = sliderBinding.textValue
textSliderValue!!.text = sliderProgress.toString() textSliderValue!!.text = String.format(
sliderBinding.textUnits.text = item.units context.getString(R.string.value_with_units),
sliderProgress.toString(),
item.units
)
sliderBinding.slider.apply { sliderBinding.slider.apply {
valueFrom = item.min.toFloat() valueFrom = item.min.toFloat()
@@ -216,7 +219,11 @@ class SettingsAdapter(
value = sliderProgress.toFloat() value = sliderProgress.toFloat()
addOnChangeListener { _: Slider, value: Float, _: Boolean -> addOnChangeListener { _: Slider, value: Float, _: Boolean ->
sliderProgress = value.toInt() sliderProgress = value.toInt()
textSliderValue!!.text = sliderProgress.toString() textSliderValue!!.text = String.format(
context.getString(R.string.value_with_units),
sliderProgress.toString(),
item.units
)
} }
} }
@@ -225,10 +232,6 @@ class SettingsAdapter(
.setView(sliderBinding.root) .setView(sliderBinding.root)
.setPositiveButton(android.R.string.ok, this) .setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener) .setNegativeButton(android.R.string.cancel, defaultCancelListener)
.setNeutralButton(R.string.slider_default) { dialog: DialogInterface, which: Int ->
sliderBinding.slider.value = item.defaultValue!!.toFloat()
onClick(dialog, which)
}
.show() .show()
} }

View File

@@ -25,12 +25,17 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE binding.textSettingDescription.visibility = View.VISIBLE
} else { } else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.VISIBLE
val epochTime = setting.value.toLong() val epochTime = setting.value.toLong()
val instant = Instant.ofEpochMilli(epochTime * 1000) val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingDescription.text = dateFormatter.format(zonedTime) binding.textSettingValue.text = dateFormatter.format(zonedTime)
}
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -23,6 +23,9 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.GONE
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -5,6 +5,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
@@ -33,4 +35,18 @@ abstract class SettingViewHolder(itemView: View, protected val adapter: Settings
abstract override fun onClick(clicked: View) abstract override fun onClick(clicked: View)
abstract override fun onLongClick(clicked: View): Boolean abstract override fun onLongClick(clicked: View): Boolean
fun setStyle(isEditable: Boolean, binding: ListItemSettingBinding) {
val opacity = if (isEditable) 1.0f else 0.5f
binding.textSettingName.alpha = opacity
binding.textSettingDescription.alpha = opacity
binding.textSettingValue.alpha = opacity
}
fun setStyle(isEditable: Boolean, binding: ListItemSettingSwitchBinding) {
binding.switchWidget.isEnabled = isEditable
val opacity = if (isEditable) 1.0f else 0.5f
binding.textSettingName.alpha = opacity
binding.textSettingDescription.alpha = opacity
}
} }

View File

@@ -17,28 +17,33 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item setting = item
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
binding.textSettingDescription.visibility = View.VISIBLE
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)
} else if (item is SingleChoiceSetting) { binding.textSettingDescription.visibility = View.VISIBLE
val resMgr = binding.textSettingDescription.context.resources } else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.VISIBLE
if (item is SingleChoiceSetting) {
val resMgr = binding.textSettingValue.context.resources
val values = resMgr.getIntArray(item.valuesId) val values = resMgr.getIntArray(item.valuesId)
for (i in values.indices) { for (i in values.indices) {
if (values[i] == item.selectedValue) { if (values[i] == item.selectedValue) {
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
return break
} }
} }
} else if (item is StringSingleChoiceSetting) { } else if (item is StringSingleChoiceSetting) {
for (i in item.values!!.indices) { for (i in item.values!!.indices) {
if (item.values[i] == item.selectedValue) { if (item.values[i] == item.selectedValue) {
binding.textSettingDescription.text = item.choices[i] binding.textSettingValue.text = item.choices[i]
return break
} }
} }
} else {
binding.textSettingDescription.visibility = View.GONE
} }
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
@@ -22,6 +23,14 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = String.format(
binding.textSettingValue.context.getString(R.string.value_with_units),
setting.selectedValue,
setting.units
)
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -22,6 +22,7 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
} else { } else {
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.textSettingValue.visibility = View.GONE
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -25,12 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
binding.textSettingDescription.text = "" binding.textSettingDescription.text = ""
binding.textSettingDescription.visibility = View.GONE binding.textSettingDescription.visibility = View.GONE
} }
binding.switchWidget.isChecked = setting.isChecked
binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked)
} }
binding.switchWidget.isChecked = setting.isChecked
binding.switchWidget.isEnabled = setting.isEditable setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@@ -19,6 +19,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController import androidx.navigation.findNavController
@@ -32,10 +33,13 @@ import org.yuzu.yuzu_emu.adapters.SetupAdapter
import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.ViewUtils
class SetupFragment : Fragment() { class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null private var _binding: FragmentSetupBinding? = null
@@ -112,14 +116,22 @@ class SetupFragment : Fragment() {
0, 0,
false, false,
R.string.give_permission, R.string.give_permission,
{ permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) }, {
notificationCallback = it
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
},
true, true,
R.string.notification_warning, R.string.notification_warning,
R.string.notification_warning_description, R.string.notification_warning_description,
0, 0,
{ {
NotificationManagerCompat.from(requireContext()) if (NotificationManagerCompat.from(requireContext())
.areNotificationsEnabled() .areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@@ -133,12 +145,22 @@ class SetupFragment : Fragment() {
R.drawable.ic_add, R.drawable.ic_add,
true, true,
R.string.select_keys, R.string.select_keys,
{ mainActivity.getProdKey.launch(arrayOf("*/*")) }, {
keyCallback = it
getProdKey.launch(arrayOf("*/*"))
},
true, true,
R.string.install_prod_keys_warning, R.string.install_prod_keys_warning,
R.string.install_prod_keys_warning_description, R.string.install_prod_keys_warning_description,
R.string.install_prod_keys_warning_help, R.string.install_prod_keys_warning_help,
{ File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() } {
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
if (file.exists()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
) )
) )
add( add(
@@ -150,9 +172,8 @@ class SetupFragment : Fragment() {
true, true,
R.string.add_games, R.string.add_games,
{ {
mainActivity.getGamesDirectory.launch( gamesDirCallback = it
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
)
}, },
true, true,
R.string.add_games_warning, R.string.add_games_warning,
@@ -163,7 +184,11 @@ class SetupFragment : Fragment() {
PreferenceManager.getDefaultSharedPreferences( PreferenceManager.getDefaultSharedPreferences(
YuzuApplication.appContext YuzuApplication.appContext
) )
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty() if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@@ -181,6 +206,13 @@ class SetupFragment : Fragment() {
) )
} }
homeViewModel.shouldPageForward.observe(viewLifecycleOwner) {
if (it) {
pageForward()
homeViewModel.setShouldPageForward(false)
}
}
binding.viewPager2.apply { binding.viewPager2.apply {
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
offscreenPageLimit = 2 offscreenPageLimit = 2
@@ -194,15 +226,15 @@ class SetupFragment : Fragment() {
super.onPageSelected(position) super.onPageSelected(position)
if (position == 1 && previousPosition == 0) { if (position == 1 && previousPosition == 0) {
showView(binding.buttonNext) ViewUtils.showView(binding.buttonNext)
showView(binding.buttonBack) ViewUtils.showView(binding.buttonBack)
} else if (position == 0 && previousPosition == 1) { } else if (position == 0 && previousPosition == 1) {
hideView(binding.buttonBack) ViewUtils.hideView(binding.buttonBack)
hideView(binding.buttonNext) ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) { } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
hideView(binding.buttonNext) ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) { } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
showView(binding.buttonNext) ViewUtils.showView(binding.buttonNext)
} }
previousPosition = position previousPosition = position
@@ -215,7 +247,8 @@ class SetupFragment : Fragment() {
// Checks if the user has completed the task on the current page // Checks if the user has completed the task on the current page
if (currentPage.hasWarning) { if (currentPage.hasWarning) {
if (currentPage.taskCompleted.invoke()) { val stepState = currentPage.stepCompleted.invoke()
if (stepState != StepState.INCOMPLETE) {
pageForward() pageForward()
return@setOnClickListener return@setOnClickListener
} }
@@ -264,9 +297,15 @@ class SetupFragment : Fragment() {
_binding = null _binding = null
} }
private lateinit var notificationCallback: SetupCallback
@RequiresApi(Build.VERSION_CODES.TIRAMISU) @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher = private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
notificationCallback.onStepCompleted()
}
if (!it && if (!it &&
!shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) { ) {
@@ -277,6 +316,27 @@ class SetupFragment : Fragment() {
} }
} }
private lateinit var keyCallback: SetupCallback
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
if (mainActivity.processKey(result)) {
keyCallback.onStepCompleted()
}
}
}
private lateinit var gamesDirCallback: SetupCallback
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
mainActivity.processGamesDir(result)
gamesDirCallback.onStepCompleted()
}
}
private fun finishSetup() { private fun finishSetup() {
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
@@ -284,33 +344,6 @@ class SetupFragment : Fragment() {
mainActivity.finishSetup(binding.root.findNavController()) mainActivity.finishSetup(binding.root.findNavController())
} }
private fun showView(view: View) {
view.apply {
alpha = 0f
visibility = View.VISIBLE
isClickable = true
}.animate().apply {
duration = 300
alpha(1f)
}.start()
}
private fun hideView(view: View) {
if (view.visibility == View.INVISIBLE) {
return
}
view.apply {
alpha = 1f
isClickable = false
}.animate().apply {
duration = 300
alpha(0f)
}.withEndAction {
view.visibility = View.INVISIBLE
}
}
fun pageForward() { fun pageForward() {
binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1 binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
} }
@@ -326,15 +359,29 @@ class SetupFragment : Fragment() {
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
) { view: View, windowInsets: WindowInsetsCompat -> ) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.setPadding(
barInsets.left + cutoutInsets.left, val leftPadding = barInsets.left + cutoutInsets.left
barInsets.top + cutoutInsets.top, val topPadding = barInsets.top + cutoutInsets.top
barInsets.right + cutoutInsets.right, val rightPadding = barInsets.right + cutoutInsets.right
barInsets.bottom + cutoutInsets.bottom val bottomPadding = barInsets.bottom + cutoutInsets.bottom
if (resources.getBoolean(R.bool.small_layout)) {
binding.viewPager2
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
binding.constraintButtons
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
} else {
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
binding.constraintButtons
.updatePadding(
left = leftPadding,
right = rightPadding,
bottom = bottomPadding
) )
}
windowInsets windowInsets
} }
} }

View File

@@ -14,6 +14,9 @@ class HomeViewModel : ViewModel() {
private val _statusBarShadeVisible = MutableLiveData(true) private val _statusBarShadeVisible = MutableLiveData(true)
val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible val statusBarShadeVisible: LiveData<Boolean> get() = _statusBarShadeVisible
private val _shouldPageForward = MutableLiveData(false)
val shouldPageForward: LiveData<Boolean> get() = _shouldPageForward
var navigatedToSetup = false var navigatedToSetup = false
init { init {
@@ -33,4 +36,8 @@ class HomeViewModel : ViewModel() {
} }
_statusBarShadeVisible.value = visible _statusBarShadeVisible.value = visible
} }
fun setShouldPageForward(pageForward: Boolean) {
_shouldPageForward.value = pageForward
}
} }

View File

@@ -10,10 +10,20 @@ data class SetupPage(
val buttonIconId: Int, val buttonIconId: Int,
val leftAlignedIcon: Boolean, val leftAlignedIcon: Boolean,
val buttonTextId: Int, val buttonTextId: Int,
val buttonAction: () -> Unit, val buttonAction: (callback: SetupCallback) -> Unit,
val hasWarning: Boolean, val hasWarning: Boolean,
val warningTitleId: Int = 0, val warningTitleId: Int = 0,
val warningDescriptionId: Int = 0, val warningDescriptionId: Int = 0,
val warningHelpLinkId: Int = 0, val warningHelpLinkId: Int = 0,
val taskCompleted: () -> Boolean = { true } val stepCompleted: () -> StepState = { StepState.UNDEFINED }
) )
interface SetupCallback {
fun onStepCompleted()
}
enum class StepState {
COMPLETE,
INCOMPLETE,
UNDEFINED
}

View File

@@ -266,10 +266,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory = val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result == null) { if (result != null) {
return@registerForActivityResult processGamesDir(result)
}
} }
fun processGamesDir(result: Uri) {
contentResolver.takePersistableUriPermission( contentResolver.takePersistableUriPermission(
result, result,
Intent.FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -292,16 +294,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getProdKey = val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) { if (result != null) {
return@registerForActivityResult processKey(result)
}
} }
fun processKey(result: Uri): Boolean {
if (FileUtil.getExtension(result) != "keys") { if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
R.string.reading_keys_failure, R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG) ).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult return false
} }
contentResolver.takePersistableUriPermission( contentResolver.takePersistableUriPermission(
@@ -324,14 +328,17 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
gamesViewModel.reloadGames(true) gamesViewModel.reloadGames(true)
return true
} else { } else {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
R.string.invalid_keys_error, R.string.invalid_keys_error,
R.string.install_keys_failure_description, R.string.install_keys_failure_description,
R.string.dumping_keys_quickstart_link R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG) ).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
} }
} }
return false
} }
val getFirmware = val getFirmware =

View File

@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.view.View
object ViewUtils {
fun showView(view: View, length: Long = 300) {
view.apply {
alpha = 0f
visibility = View.VISIBLE
isClickable = true
}.animate().apply {
duration = length
alpha(1f)
}.start()
}
fun hideView(view: View, length: Long = 300) {
if (view.visibility == View.INVISIBLE) {
return
}
view.apply {
alpha = 1f
isClickable = false
}.animate().apply {
duration = length
alpha(0f)
}.withEndAction {
view.visibility = View.INVISIBLE
}.start()
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_root" android:id="@+id/setup_root"
@@ -8,19 +8,24 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_alignParentTop="true"
app:layout_constraintEnd_toEndOf="parent" android:layout_alignParentBottom="true"
app:layout_constraintStart_toStartOf="parent" android:clipToPadding="false" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="8dp">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_next" android:id="@+id/button_next"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/next" android:text="@string/next"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@@ -31,10 +36,11 @@
style="@style/Widget.Material3.Button.TextButton" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/back" android:text="@string/back"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@@ -21,45 +21,76 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1">
android:orientation="vertical"
android:gravity="center">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.DisplaySmall"
android:id="@+id/text_title" android:id="@+id/text_title"
android:layout_width="match_parent" style="@style/TextAppearance.Material3.DisplaySmall"
android:layout_height="wrap_content" android:layout_width="0dp"
android:textAlignment="center" android:layout_height="0dp"
android:gravity="center"
android:textColor="?attr/colorOnSurface" android:textColor="?attr/colorOnSurface"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/text_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="2"
tools:text="@string/welcome" /> tools:text="@string/welcome" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleLarge"
android:id="@+id/text_description" android:id="@+id/text_description"
android:layout_width="match_parent" style="@style/TextAppearance.Material3.TitleLarge"
android:layout_height="wrap_content" android:layout_width="0dp"
android:layout_marginTop="16dp" android:layout_height="0dp"
android:paddingHorizontal="32dp" android:gravity="center"
android:textAlignment="center" android:textSize="20sp"
android:textSize="26sp" android:paddingHorizontal="16dp"
app:lineHeight="40sp" app:layout_constraintBottom_toTopOf="@+id/button_action"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title"
app:layout_constraintVertical_weight="2"
app:lineHeight="30sp"
tools:text="@string/welcome_description" /> tools:text="@string/welcome_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingHorizontal="16dp"
android:paddingBottom="20dp"
android:gravity="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="@string/step_complete"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_action" android:id="@+id/button_action"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="56dp"
android:layout_marginTop="32dp" android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:textSize="20sp" android:textSize="20sp"
app:iconSize="24sp"
app:iconGravity="end" app:iconGravity="end"
app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
tools:text="Get started" /> tools:text="Get started" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@@ -5,23 +5,16 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_value" android:id="@+id/text_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/spacing_medlarge" android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge" android:layout_marginTop="@dimen/spacing_medlarge"
tools:text="75" /> tools:text="75%" />
<TextView
android:id="@+id/text_units"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/text_value"
android:layout_toEndOf="@+id/text_value"
tools:text="%" />
<com.google.android.material.slider.Slider <com.google.android.material.slider.Slider
android:id="@+id/slider" android:id="@+id/slider"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/setup_root" android:id="@+id/setup_root"
@@ -8,35 +8,39 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:clipToPadding="false" android:layout_above="@+id/constraint_buttons"
android:layout_marginBottom="16dp" android:layout_alignParentTop="true"
app:layout_constraintBottom_toTopOf="@+id/button_next" android:clipToPadding="false" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" <androidx.constraintlayout.widget.ConstraintLayout
app:layout_constraintTop_toTopOf="parent" /> android:id="@+id/constraint_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_alignParentBottom="true">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_next" android:id="@+id/button_next"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="12dp"
android:text="@string/next" android:text="@string/next"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_back" android:id="@+id/button_back"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="12dp"
android:text="@string/back" android:text="@string/back"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
@@ -11,31 +12,40 @@
android:minHeight="72dp" android:minHeight="72dp"
android:padding="@dimen/spacing_large"> android:padding="@dimen/spacing_large">
<com.google.android.material.textview.MaterialTextView <LinearLayout
style="@style/TextAppearance.Material3.HeadlineMedium" android:layout_width="match_parent"
android:id="@+id/text_setting_name" android:layout_height="wrap_content"
android:layout_width="0dp" android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.Material3.HeadlineMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:textSize="16sp"
android:textAlignment="viewStart" android:textAlignment="viewStart"
app:lineHeight="28dp" android:textSize="16sp"
app:lineHeight="22dp"
tools:text="Setting Name" /> tools:text="Setting Name" />
<TextView <com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodySmall"
android:id="@+id/text_setting_description" android:id="@+id/text_setting_description"
android:layout_width="wrap_content" style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_alignStart="@+id/text_setting_name"
android:layout_below="@+id/text_setting_name"
android:layout_marginTop="@dimen/spacing_small" android:layout_marginTop="@dimen/spacing_small"
android:visibility="visible"
android:textAlignment="viewStart" android:textAlignment="viewStart"
tools:text="@string/app_disclaimer" /> tools:text="@string/app_disclaimer" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
android:textStyle="bold"
tools:text="1x" />
</LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@@ -21,11 +21,12 @@
app:layout_constraintVertical_chainStyle="spread" app:layout_constraintVertical_chainStyle="spread"
app:layout_constraintWidth_max="220dp" app:layout_constraintWidth_max="220dp"
app:layout_constraintWidth_min="110dp" app:layout_constraintWidth_min="110dp"
app:layout_constraintVertical_weight="3" /> app:layout_constraintVertical_weight="3"
tools:src="@drawable/ic_notification" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_title" android:id="@+id/text_title"
style="@style/TextAppearance.Material3.DisplayMedium" style="@style/TextAppearance.Material3.DisplaySmall"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
@@ -44,23 +45,42 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
android:textSize="26sp" android:textSize="20sp"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_action" app:layout_constraintBottom_toTopOf="@+id/button_action"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title" app:layout_constraintTop_toBottomOf="@+id/text_title"
app:layout_constraintVertical_weight="2" app:layout_constraintVertical_weight="2"
app:lineHeight="40sp" app:lineHeight="30sp"
tools:text="@string/welcome_description" /> tools:text="@string/welcome_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:textAlignment="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="@string/step_complete"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_action" android:id="@+id/button_action"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="56dp" android:layout_height="56dp"
android:textSize="20sp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="48dp" android:layout_marginBottom="48dp"
android:textSize="20sp"
app:iconGravity="end" app:iconGravity="end"
app:iconSize="24sp" app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

@@ -29,6 +29,7 @@
<string name="back">Back</string> <string name="back">Back</string>
<string name="add_games">Add Games</string> <string name="add_games">Add Games</string>
<string name="add_games_description">Select your games folder</string> <string name="add_games_description">Select your games folder</string>
<string name="step_complete">Complete!</string>
<!-- Home strings --> <!-- Home strings -->
<string name="home_games">Games</string> <string name="home_games">Games</string>
@@ -149,6 +150,7 @@
<string name="frame_limit_slider">Limit speed percent</string> <string name="frame_limit_slider">Limit speed percent</string>
<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> <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>
<string name="cpu_accuracy">CPU accuracy</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings --> <!-- System settings strings -->
<string name="use_docked_mode">Docked Mode</string> <string name="use_docked_mode">Docked Mode</string>

View File

@@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <functional>
#include <string> #include <string>
#include <vector>
#include "common/settings_common.h" #include "common/settings_common.h"
namespace Settings { namespace Settings {

View File

@@ -12,8 +12,8 @@ namespace Settings {
template <typename T> template <typename T>
struct EnumMetadata { struct EnumMetadata {
static constexpr std::vector<std::pair<std::string, T>> Canonicalizations(); static std::vector<std::pair<std::string, T>> Canonicalizations();
static constexpr u32 Index(); static u32 Index();
}; };
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__)) #define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
@@ -66,11 +66,11 @@ struct EnumMetadata {
#define ENUM(NAME, ...) \ #define ENUM(NAME, ...) \
enum class NAME : u32 { __VA_ARGS__ }; \ enum class NAME : u32 { __VA_ARGS__ }; \
template <> \ template <> \
constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \ inline std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
return {PAIR(NAME, __VA_ARGS__)}; \ return {PAIR(NAME, __VA_ARGS__)}; \
} \ } \
template <> \ template <> \
constexpr u32 EnumMetadata<NAME>::Index() { \ inline u32 EnumMetadata<NAME>::Index() { \
return __COUNTER__; \ return __COUNTER__; \
} }
@@ -85,7 +85,7 @@ enum class AudioEngine : u32 {
}; };
template <> template <>
constexpr std::vector<std::pair<std::string, AudioEngine>> inline std::vector<std::pair<std::string, AudioEngine>>
EnumMetadata<AudioEngine>::Canonicalizations() { EnumMetadata<AudioEngine>::Canonicalizations() {
return { return {
{"auto", AudioEngine::Auto}, {"auto", AudioEngine::Auto},
@@ -96,7 +96,7 @@ EnumMetadata<AudioEngine>::Canonicalizations() {
} }
template <> template <>
constexpr u32 EnumMetadata<AudioEngine>::Index() { inline u32 EnumMetadata<AudioEngine>::Index() {
// This is just a sufficiently large number that is more than the number of other enums declared // This is just a sufficiently large number that is more than the number of other enums declared
// here // here
return 100; return 100;
@@ -147,7 +147,7 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
template <typename Type> template <typename Type>
constexpr std::string CanonicalizeEnum(Type id) { inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) { for (auto& [name, value] : group) {
if (value == id) { if (value == id) {
@@ -158,7 +158,7 @@ constexpr std::string CanonicalizeEnum(Type id) {
} }
template <typename Type> template <typename Type>
constexpr Type ToEnum(const std::string& canonicalization) { inline Type ToEnum(const std::string& canonicalization) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();
for (auto& [name, value] : group) { for (auto& [name, value] : group) {
if (name == canonicalization) { if (name == canonicalization) {

View File

@@ -190,7 +190,7 @@ public:
} }
} }
[[nodiscard]] std::string constexpr Canonicalize() const override final { [[nodiscard]] std::string Canonicalize() const override final {
if constexpr (std::is_enum_v<Type>) { if constexpr (std::is_enum_v<Type>) {
return CanonicalizeEnum(this->GetValue()); return CanonicalizeEnum(this->GetValue());
} else { } else {
@@ -256,11 +256,11 @@ public:
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata * @param other_setting_ A second Setting to associate to this one in metadata
*/ */
template <typename T = BasicSetting>
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
Category category_, u32 specialization_ = Specialization::Default, Category category_, u32 specialization_ = Specialization::Default,
bool save_ = true, bool runtime_modifiable_ = false, bool save_ = true, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr) typename std::enable_if<!ranged, T*>::type other_setting_ = nullptr)
requires(!ranged)
: Setting<Type, false>{ : Setting<Type, false>{
linkage, default_val, name, category_, specialization_, linkage, default_val, name, category_, specialization_,
save_, runtime_modifiable_, other_setting_} { save_, runtime_modifiable_, other_setting_} {
@@ -282,12 +282,12 @@ public:
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
* @param other_setting_ A second Setting to associate to this one in metadata * @param other_setting_ A second Setting to associate to this one in metadata
*/ */
template <typename T = BasicSetting>
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
const Type& max_val, const std::string& name, Category category_, const Type& max_val, const std::string& name, Category category_,
u32 specialization_ = Specialization::Default, bool save_ = true, u32 specialization_ = Specialization::Default, bool save_ = true,
bool runtime_modifiable_ = false, bool runtime_modifiable_ = false,
BasicSetting* other_setting_ = nullptr) typename std::enable_if<ranged, T*>::type other_setting_ = nullptr)
requires(ranged)
: Setting<Type, true>{linkage, default_val, min_val, : Setting<Type, true>{linkage, default_val, min_val,
max_val, name, category_, max_val, name, category_,
specialization_, save_, runtime_modifiable_, specialization_, save_, runtime_modifiable_,

View File

@@ -263,6 +263,23 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
std::vector<u8> mem(size); std::vector<u8> mem(size);
if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr);
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
// Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it->second;
// Calculate where to start writing to the output buffer.
const size_t output_offset = it->first - addr;
// Calculate how many bytes to write.
// The loop condition ensures output_offset < size.
const size_t n = std::min<size_t>(size - output_offset, sizeof(u32));
// Write the bytes to the output buffer.
std::memcpy(mem.data() + output_offset, &original_bytes, n);
}
SendReply(Common::HexToString(mem)); SendReply(Common::HexToString(mem));
} else { } else {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);

View File

@@ -289,6 +289,19 @@ enum class GyroscopeZeroDriftMode : u32 {
Tight = 2, Tight = 2,
}; };
// This is nn::settings::system::TouchScreenMode
enum class TouchScreenMode : u32 {
Stylus = 0,
Standard = 1,
};
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::NpadStyleTag // This is nn::hid::NpadStyleTag
struct NpadStyleTag { struct NpadStyleTag {
union { union {
@@ -334,6 +347,14 @@ struct TouchState {
}; };
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES(0xF);
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
"TouchScreenConfigurationForNx is an invalid size");
struct NpadColor { struct NpadColor {
u8 r{}; u8 r{};
u8 g{}; u8 g{};
@@ -662,6 +683,11 @@ struct MouseState {
}; };
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
struct UniquePadId {
u64 id;
};
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
/// Converts a NpadIdType to an array index. /// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) { constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) { switch (npad_id_type) {

View File

@@ -19,13 +19,7 @@ public:
void Initialize(); void Initialize();
void Finalize(); void Finalize();
s64 GetCount() const { s64 GetTick() const;
return GetTick();
}
void RegisterTask(KTimerTask* task, s64 time_from_now) {
this->RegisterAbsoluteTask(task, GetTick() + time_from_now);
}
void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) { void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
KScopedDisableDispatch dd{m_kernel}; KScopedDisableDispatch dd{m_kernel};
@@ -42,7 +36,6 @@ private:
void EnableInterrupt(s64 wakeup_time); void EnableInterrupt(s64 wakeup_time);
void DisableInterrupt(); void DisableInterrupt();
bool GetInterruptEnabled(); bool GetInterruptEnabled();
s64 GetTick() const;
void DoTask(); void DoTask();
private: private:

View File

@@ -5,6 +5,7 @@
#include "common/overflow.h" #include "common/overflow.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
@@ -15,9 +16,7 @@ KResourceLimit::KResourceLimit(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{m_kernel}, m_cond_var{m_kernel} {} : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{m_kernel}, m_cond_var{m_kernel} {}
KResourceLimit::~KResourceLimit() = default; KResourceLimit::~KResourceLimit() = default;
void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing) { void KResourceLimit::Initialize() {}
m_core_timing = core_timing;
}
void KResourceLimit::Finalize() {} void KResourceLimit::Finalize() {}
@@ -86,7 +85,7 @@ Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
} }
bool KResourceLimit::Reserve(LimitableResource which, s64 value) { bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
return Reserve(which, value, m_core_timing->GetGlobalTimeNs().count() + DefaultTimeout); return Reserve(which, value, m_kernel.HardwareTimer().GetTick() + DefaultTimeout);
} }
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
@@ -117,7 +116,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
} }
if (m_current_hints[index] + value <= m_limit_values[index] && if (m_current_hints[index] + value <= m_limit_values[index] &&
(timeout < 0 || m_core_timing->GetGlobalTimeNs().count() < timeout)) { (timeout < 0 || m_kernel.HardwareTimer().GetTick() < timeout)) {
m_waiter_count++; m_waiter_count++;
m_cond_var.Wait(std::addressof(m_lock), timeout, false); m_cond_var.Wait(std::addressof(m_lock), timeout, false);
m_waiter_count--; m_waiter_count--;
@@ -154,7 +153,7 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) { KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
auto* resource_limit = KResourceLimit::Create(system.Kernel()); auto* resource_limit = KResourceLimit::Create(system.Kernel());
resource_limit->Initialize(std::addressof(system.CoreTiming())); resource_limit->Initialize();
// Initialize default resource limit values. // Initialize default resource limit values.
// TODO(bunnei): These values are the system defaults, the limits for service processes are // TODO(bunnei): These values are the system defaults, the limits for service processes are

View File

@@ -31,7 +31,7 @@ public:
explicit KResourceLimit(KernelCore& kernel); explicit KResourceLimit(KernelCore& kernel);
~KResourceLimit() override; ~KResourceLimit() override;
void Initialize(const Core::Timing::CoreTiming* core_timing); void Initialize();
void Finalize() override; void Finalize() override;
s64 GetLimitValue(LimitableResource which) const; s64 GetLimitValue(LimitableResource which) const;
@@ -57,7 +57,6 @@ private:
mutable KLightLock m_lock; mutable KLightLock m_lock;
s32 m_waiter_count{}; s32 m_waiter_count{};
KLightConditionVariable m_cond_var; KLightConditionVariable m_cond_var;
const Core::Timing::CoreTiming* m_core_timing{};
}; };
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size); KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);

View File

@@ -28,7 +28,7 @@ public:
~KScopedSchedulerLockAndSleep() { ~KScopedSchedulerLockAndSleep() {
// Register the sleep. // Register the sleep.
if (m_timeout_tick > 0) { if (m_timeout_tick > 0) {
m_timer->RegisterTask(m_thread, m_timeout_tick); m_timer->RegisterAbsoluteTask(m_thread, m_timeout_tick);
} }
// Unlock the scheduler. // Unlock the scheduler.

View File

@@ -231,7 +231,7 @@ struct KernelCore::Impl {
void InitializeSystemResourceLimit(KernelCore& kernel, void InitializeSystemResourceLimit(KernelCore& kernel,
const Core::Timing::CoreTiming& core_timing) { const Core::Timing::CoreTiming& core_timing) {
system_resource_limit = KResourceLimit::Create(system.Kernel()); system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing); system_resource_limit->Initialize();
KResourceLimit::Register(kernel, system_resource_limit); KResourceLimit::Register(kernel, system_resource_limit);
const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()}; const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
@@ -52,7 +53,7 @@ Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_typ
if (timeout_ns > 0) { if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns); const s64 offset_tick(timeout_ns);
if (offset_tick > 0) { if (offset_tick > 0) {
timeout = offset_tick + 2; timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) { if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max(); timeout = std::numeric_limits<s64>::max();
} }

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
@@ -25,7 +26,7 @@ Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u
if (timeout_ns > 0) { if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns); const s64 offset_tick(timeout_ns);
if (offset_tick > 0) { if (offset_tick > 0) {
timeout = offset_tick + 2; timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) { if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max(); timeout = std::numeric_limits<s64>::max();
} }

View File

@@ -5,6 +5,7 @@
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
@@ -82,12 +83,29 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
R_TRY(session->SendReply()); R_TRY(session->SendReply());
} }
// Convert the timeout from nanoseconds to ticks.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
s64 timeout;
if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns);
if (offset_tick > 0) {
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = timeout_ns;
}
// Wait for a message. // Wait for a message.
while (true) { while (true) {
// Wait for an object. // Wait for an object.
s32 index; s32 index;
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
num_handles, timeout_ns); num_handles, timeout);
if (result == ResultTimedOut) { if (result == ResultTimedOut) {
R_RETURN(result); R_RETURN(result);
} }

View File

@@ -21,7 +21,7 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
SCOPE_EXIT({ resource_limit->Close(); }); SCOPE_EXIT({ resource_limit->Close(); });
// Initialize the resource limit. // Initialize the resource limit.
resource_limit->Initialize(std::addressof(system.CoreTiming())); resource_limit->Initialize();
// Register the limit. // Register the limit.
KResourceLimit::Register(kernel, resource_limit); KResourceLimit::Register(kernel, resource_limit);

View File

@@ -4,6 +4,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
@@ -83,9 +84,20 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
} }
}); });
// Convert the timeout from nanoseconds to ticks.
s64 timeout;
if (timeout_ns > 0) {
u64 ticks = kernel.HardwareTimer().GetTick();
ticks += timeout_ns;
ticks += 2;
timeout = ticks;
} else {
timeout = timeout_ns;
}
// Wait on the objects. // Wait on the objects.
Result res = Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout);
KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns);
R_SUCCEED_IF(res == ResultSessionClosed); R_SUCCEED_IF(res == ResultSessionClosed);
R_RETURN(res); R_RETURN(res);

View File

@@ -4,6 +4,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
@@ -42,9 +43,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority); R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
// Reserve a new thread from the process resource limit (waiting up to 100ms). // Reserve a new thread from the process resource limit (waiting up to 100ms).
KScopedResourceReservation thread_reservation( KScopedResourceReservation thread_reservation(std::addressof(process),
std::addressof(process), LimitableResource::ThreadCountMax, 1, LimitableResource::ThreadCountMax, 1,
system.CoreTiming().GetGlobalTimeNs().count() + 100000000); kernel.HardwareTimer().GetTick() + 100000000);
R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached);
// Create the thread. // Create the thread.
@@ -102,20 +103,31 @@ void ExitThread(Core::System& system) {
} }
/// Sleep the current thread /// Sleep the current thread
void SleepThread(Core::System& system, s64 nanoseconds) { void SleepThread(Core::System& system, s64 ns) {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); const auto yield_type = static_cast<Svc::YieldType>(ns);
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); LOG_TRACE(Kernel_SVC, "called nanoseconds={}", ns);
// When the input tick is positive, sleep. // When the input tick is positive, sleep.
if (nanoseconds > 0) { if (ns > 0) {
// Convert the timeout from nanoseconds to ticks. // Convert the timeout from nanoseconds to ticks.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization... // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
s64 timeout;
const s64 offset_tick(ns);
if (offset_tick > 0) {
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
// Sleep. // Sleep.
// NOTE: Nintendo does not check the result of this sleep. // NOTE: Nintendo does not check the result of this sleep.
static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); static_cast<void>(GetCurrentThread(kernel).Sleep(timeout));
} else if (yield_type == Svc::YieldType::WithoutCoreMigration) { } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
KScheduler::YieldWithoutCoreMigration(kernel); KScheduler::YieldWithoutCoreMigration(kernel);
} else if (yield_type == Svc::YieldType::WithCoreMigration) { } else if (yield_type == Svc::YieldType::WithCoreMigration) {
@@ -124,7 +136,6 @@ void SleepThread(Core::System& system, s64 nanoseconds) {
KScheduler::YieldToAnyThread(kernel); KScheduler::YieldToAnyThread(kernel);
} else { } else {
// Nintendo does nothing at all if an otherwise invalid value is passed. // Nintendo does nothing at all if an otherwise invalid value is passed.
ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
} }
} }

View File

@@ -16,22 +16,6 @@ class EmulatedConsole;
namespace Service::HID { namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase { class Controller_Touchscreen final : public ControllerBase {
public: public:
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_); explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override; ~Controller_Touchscreen() override;

View File

@@ -2368,7 +2368,7 @@ void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()}; const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
const auto applet_resource_user_id{rp.Pop<u64>()}; const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
@@ -2543,7 +2543,8 @@ public:
class HidSys final : public ServiceFramework<HidSys> { class HidSys final : public ServiceFramework<HidSys> {
public: public:
explicit HidSys(Core::System& system_) : ServiceFramework{system_, "hid:sys"} { explicit HidSys(Core::System& system_)
: ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{31, nullptr, "SendKeyboardLockKeyEvent"}, {31, nullptr, "SendKeyboardLockKeyEvent"},
@@ -2568,7 +2569,7 @@ public:
{303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, nullptr, "EnableAssigningSingleOnSlSrPress"}, {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
{305, nullptr, "DisableAssigningSingleOnSlSrPress"}, {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
{306, nullptr, "GetLastActiveNpad"}, {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"}, {307, nullptr, "GetNpadSystemExtStyle"},
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"}, {309, nullptr, "GetNpadFullKeyGripColor"},
@@ -2624,7 +2625,7 @@ public:
{700, nullptr, "ActivateUniquePad"}, {700, nullptr, "ActivateUniquePad"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"}, {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"}, {703, nullptr, "GetUniquePadIds"},
{751, nullptr, "AcquireJoyDetachOnBluetoothOffEventHandle"}, {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"}, {800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
{802, nullptr, "ResetSixAxisSensorCalibrationValues"}, {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
@@ -2650,7 +2651,7 @@ public:
{830, nullptr, "SetNotificationLedPattern"}, {830, nullptr, "SetNotificationLedPattern"},
{831, nullptr, "SetNotificationLedPatternWithTimeout"}, {831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"}, {832, nullptr, "PrepareHidsForNotificationWake"},
{850, nullptr, "IsUsbFullKeyControllerEnabled"}, {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"}, {851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"}, {852, nullptr, "IsUsbConnected"},
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
@@ -2682,7 +2683,7 @@ public:
{1150, nullptr, "SetTouchScreenMagnification"}, {1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"}, {1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, nullptr, "SetTouchScreenDefaultConfiguration"}, {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
{1153, nullptr, "GetTouchScreenDefaultConfiguration"}, {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"}, {1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"}, {1155, nullptr, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
@@ -2749,6 +2750,8 @@ public:
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
} }
private: private:
@@ -2760,17 +2763,66 @@ private:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void GetLastActiveNpad(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(Core::HID::NpadIdType::Handheld);
}
void GetUniquePadsFromNpad(HLERequestContext& ctx) { void GetUniquePadsFromNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()}; const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
const s64 total_entries = 0;
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
const std::vector<Core::HID::UniquePadId> unique_pads{};
ctx.WriteBuffer(unique_pads);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push(total_entries); rb.Push(static_cast<u32>(unique_pads.size()));
} }
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
}
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
const bool is_enabled = false;
LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
Core::HID::TouchScreenConfigurationForNx touchscreen_config{
.mode = Core::HID::TouchScreenModeForNx::Finger,
};
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(touchscreen_config);
}
Kernel::KEvent* joy_detach_event;
KernelHelpers::ServiceContext service_context;
}; };
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {

View File

@@ -449,6 +449,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
case NativeWindowScalingMode::ScaleToWindow: case NativeWindowScalingMode::ScaleToWindow:
case NativeWindowScalingMode::ScaleCrop: case NativeWindowScalingMode::ScaleCrop:
case NativeWindowScalingMode::NoScaleCrop: case NativeWindowScalingMode::NoScaleCrop:
case NativeWindowScalingMode::PreserveAspectRatio:
break; break;
default: default:
LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode); LOG_ERROR(Service_Nvnflinger, "unknown scaling mode {}", scaling_mode);

View File

@@ -41,6 +41,7 @@ enum class NativeWindowScalingMode : s32 {
ScaleToWindow = 1, ScaleToWindow = 1,
ScaleCrop = 2, ScaleCrop = 2,
NoScaleCrop = 3, NoScaleCrop = 3,
PreserveAspectRatio = 4,
}; };
/// Transform parameter for QueueBuffer /// Transform parameter for QueueBuffer

View File

@@ -18,7 +18,9 @@ enum class Errno : u32 {
AGAIN = 11, AGAIN = 11,
INVAL = 22, INVAL = 22,
MFILE = 24, MFILE = 24,
PIPE = 32,
MSGSIZE = 90, MSGSIZE = 90,
CONNABORTED = 103,
CONNRESET = 104, CONNRESET = 104,
NOTCONN = 107, NOTCONN = 107,
TIMEDOUT = 110, TIMEDOUT = 110,

View File

@@ -23,10 +23,14 @@ Errno Translate(Network::Errno value) {
return Errno::INVAL; return Errno::INVAL;
case Network::Errno::MFILE: case Network::Errno::MFILE:
return Errno::MFILE; return Errno::MFILE;
case Network::Errno::PIPE:
return Errno::PIPE;
case Network::Errno::NOTCONN: case Network::Errno::NOTCONN:
return Errno::NOTCONN; return Errno::NOTCONN;
case Network::Errno::TIMEDOUT: case Network::Errno::TIMEDOUT:
return Errno::TIMEDOUT; return Errno::TIMEDOUT;
case Network::Errno::CONNABORTED:
return Errno::CONNABORTED;
case Network::Errno::CONNRESET: case Network::Errno::CONNRESET:
return Errno::CONNRESET; return Errno::CONNRESET;
case Network::Errno::INPROGRESS: case Network::Errno::INPROGRESS:

View File

@@ -100,7 +100,7 @@ public:
Result DoHandshake() override { Result DoHandshake() override {
OSStatus status = SSLHandshake(context); OSStatus status = SSLHandshake(context);
return HandleReturn("SSLHandshake", 0, status).Code(); return HandleReturn("SSLHandshake", 0, status);
} }
Result Read(size_t* out_size, std::span<u8> data) override { Result Read(size_t* out_size, std::span<u8> data) override {

View File

@@ -39,6 +39,11 @@ namespace Network {
namespace { namespace {
enum class CallType {
Send,
Other,
};
#ifdef _WIN32 #ifdef _WIN32
using socklen_t = int; using socklen_t = int;
@@ -96,7 +101,7 @@ bool EnableNonBlock(SOCKET fd, bool enable) {
return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
} }
Errno TranslateNativeError(int e) { Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
switch (e) { switch (e) {
case 0: case 0:
return Errno::SUCCESS; return Errno::SUCCESS;
@@ -112,6 +117,14 @@ Errno TranslateNativeError(int e) {
return Errno::AGAIN; return Errno::AGAIN;
case WSAECONNREFUSED: case WSAECONNREFUSED:
return Errno::CONNREFUSED; return Errno::CONNREFUSED;
case WSAECONNABORTED:
if (call_type == CallType::Send) {
// Winsock yields WSAECONNABORTED from `send` in situations where Unix
// systems, and actual Switches, yield EPIPE.
return Errno::PIPE;
} else {
return Errno::CONNABORTED;
}
case WSAECONNRESET: case WSAECONNRESET:
return Errno::CONNRESET; return Errno::CONNRESET;
case WSAEHOSTUNREACH: case WSAEHOSTUNREACH:
@@ -198,7 +211,7 @@ bool EnableNonBlock(int fd, bool enable) {
return fcntl(fd, F_SETFL, flags) == 0; return fcntl(fd, F_SETFL, flags) == 0;
} }
Errno TranslateNativeError(int e) { Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
switch (e) { switch (e) {
case 0: case 0:
return Errno::SUCCESS; return Errno::SUCCESS;
@@ -208,6 +221,10 @@ Errno TranslateNativeError(int e) {
return Errno::INVAL; return Errno::INVAL;
case EMFILE: case EMFILE:
return Errno::MFILE; return Errno::MFILE;
case EPIPE:
return Errno::PIPE;
case ECONNABORTED:
return Errno::CONNABORTED;
case ENOTCONN: case ENOTCONN:
return Errno::NOTCONN; return Errno::NOTCONN;
case EAGAIN: case EAGAIN:
@@ -236,13 +253,13 @@ Errno TranslateNativeError(int e) {
#endif #endif
Errno GetAndLogLastError() { Errno GetAndLogLastError(CallType call_type = CallType::Other) {
#ifdef _WIN32 #ifdef _WIN32
int e = WSAGetLastError(); int e = WSAGetLastError();
#else #else
int e = errno; int e = errno;
#endif #endif
const Errno err = TranslateNativeError(e); const Errno err = TranslateNativeError(e, call_type);
if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) { if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) {
// These happen during normal operation, so only log them at debug level. // These happen during normal operation, so only log them at debug level.
LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e));
@@ -476,7 +493,13 @@ NetworkInstance::~NetworkInstance() {
std::optional<IPv4Address> GetHostIPv4Address() { std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface(); const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) { if (!network_interface.has_value()) {
LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface"); // Only print the error once to avoid log spam
static bool print_error = true;
if (print_error) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
print_error = false;
}
return {}; return {};
} }
@@ -725,13 +748,17 @@ std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
ASSERT(flags == 0); ASSERT(flags == 0);
int native_flags = 0;
#if YUZU_UNIX
native_flags |= MSG_NOSIGNAL; // do not send us SIGPIPE
#endif
const auto result = send(fd, reinterpret_cast<const char*>(message.data()), const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
static_cast<int>(message.size()), 0); static_cast<int>(message.size()), native_flags);
if (result != SOCKET_ERROR) { if (result != SOCKET_ERROR) {
return {static_cast<s32>(result), Errno::SUCCESS}; return {static_cast<s32>(result), Errno::SUCCESS};
} }
return {-1, GetAndLogLastError()}; return {-1, GetAndLogLastError(CallType::Send)};
} }
std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message, std::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,
return {static_cast<s32>(result), Errno::SUCCESS}; return {static_cast<s32>(result), Errno::SUCCESS};
} }
return {-1, GetAndLogLastError()}; return {-1, GetAndLogLastError(CallType::Send)};
} }
Errno Socket::Close() { Errno Socket::Close() {

View File

@@ -33,10 +33,12 @@ enum class Errno {
BADF, BADF,
INVAL, INVAL,
MFILE, MFILE,
PIPE,
NOTCONN, NOTCONN,
AGAIN, AGAIN,
CONNREFUSED, CONNREFUSED,
CONNRESET, CONNRESET,
CONNABORTED,
HOSTUNREACH, HOSTUNREACH,
NETDOWN, NETDOWN,
NETUNREACH, NETUNREACH,

View File

@@ -200,7 +200,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
}); });
if (res == network_interfaces.end()) { if (res == network_interfaces.end()) {
LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); // Only print the error once to avoid log spam
static bool print_error = true;
if (print_error) {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"",
selected_network_interface);
print_error = false;
}
return std::nullopt; return std::nullopt;
} }

View File

@@ -509,9 +509,9 @@ class GuestMemory {
public: public:
GuestMemory() = delete; GuestMemory() = delete;
explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_, explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: memory{memory_}, addr{addr_}, size{size_} { : m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) { if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup); Read(addr, size, backup);
@@ -521,89 +521,97 @@ public:
~GuestMemory() = default; ~GuestMemory() = default;
T* data() noexcept { T* data() noexcept {
return data_span.data(); return m_data_span.data();
} }
const T* data() const noexcept { const T* data() const noexcept {
return data_span.data(); return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
} }
[[nodiscard]] T* begin() noexcept { [[nodiscard]] T* begin() noexcept {
return data(); return this->data();
} }
[[nodiscard]] const T* begin() const noexcept { [[nodiscard]] const T* begin() const noexcept {
return data(); return this->data();
} }
[[nodiscard]] T* end() noexcept { [[nodiscard]] T* end() noexcept {
return data() + size; return this->data() + this->size();
} }
[[nodiscard]] const T* end() const noexcept { [[nodiscard]] const T* end() const noexcept {
return data() + size; return this->data() + this->size();
} }
T& operator[](size_t index) noexcept { T& operator[](size_t index) noexcept {
return data_span[index]; return m_data_span[index];
} }
const T& operator[](size_t index) const noexcept { const T& operator[](size_t index) const noexcept {
return data_span[index]; return m_data_span[index];
} }
void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept { void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
addr = addr_; m_addr = addr;
size = size_; m_size = size;
addr_changed = true; m_addr_changed = true;
} }
std::span<T> Read(u64 addr_, std::size_t size_, std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept { Common::ScratchBuffer<T>* backup = nullptr) noexcept {
addr = addr_; m_addr = addr;
size = size_; m_size = size;
if (size == 0) { if (m_size == 0) {
is_data_copy = true; m_is_data_copy = true;
return {}; return {};
} }
if (TrySetSpan()) { if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.FlushRegion(addr, size * sizeof(T)); m_memory.FlushRegion(m_addr, this->size_bytes());
} }
} else { } else {
if (backup) { if (backup) {
backup->resize_destructive(size); backup->resize_destructive(this->size());
data_span = *backup; m_data_span = *backup;
} else { } else {
data_copy.resize(size); m_data_copy.resize(this->size());
data_span = std::span(data_copy); m_data_span = std::span(m_data_copy);
} }
is_data_copy = true; m_is_data_copy = true;
span_valid = true; m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.ReadBlock(addr, data_span.data(), size * sizeof(T)); m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else { } else {
memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T)); m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
} }
} }
return data_span; return m_data_span;
} }
void Write(std::span<T> write_data) noexcept { void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
memory.WriteBlock(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else { } else {
memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T)); m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
} }
} }
bool TrySetSpan() noexcept { bool TrySetSpan() noexcept {
if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) { if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
data_span = {reinterpret_cast<T*>(ptr), size}; m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
span_valid = true; m_span_valid = true;
return true; return true;
} }
return false; return false;
@@ -611,36 +619,36 @@ public:
protected: protected:
bool IsDataCopy() const noexcept { bool IsDataCopy() const noexcept {
return is_data_copy; return m_is_data_copy;
} }
bool AddressChanged() const noexcept { bool AddressChanged() const noexcept {
return addr_changed; return m_addr_changed;
} }
M& memory; M& m_memory;
u64 addr; u64 m_addr{};
size_t size; size_t m_size{};
std::span<T> data_span{}; std::span<T> m_data_span{};
std::vector<T> data_copy; std::vector<T> m_data_copy{};
bool span_valid{false}; bool m_span_valid{false};
bool is_data_copy{false}; bool m_is_data_copy{false};
bool addr_changed{false}; bool m_addr_changed{false};
}; };
template <typename M, typename T, GuestMemoryFlags FLAGS> template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public: public:
GuestMemoryScoped() = delete; GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_, explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) { : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) { if (!this->TrySetSpan()) {
if (backup) { if (backup) {
this->data_span = *backup; this->m_data_span = *backup;
this->span_valid = true; this->m_span_valid = true;
this->is_data_copy = true; this->m_is_data_copy = true;
} }
} }
} }
@@ -648,24 +656,21 @@ public:
~GuestMemoryScoped() { ~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) { if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size == 0) [[unlikely]] { if (this->size() == 0) [[unlikely]] {
return; return;
} }
if (this->AddressChanged() || this->IsDataCopy()) { if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->span_valid); ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) { if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->memory.WriteBlockCached(this->addr, this->data_span.data(), this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->memory.WriteBlock(this->addr, this->data_span.data(), this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} else { } else {
this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(), this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
this->size * sizeof(T));
} }
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) { } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->memory.InvalidateRegion(this->addr, this->size * sizeof(T)); this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
} }
} }
} }

View File

@@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/polyfill_ranges.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/memory.h" #include "core/memory.h"

View File

@@ -50,6 +50,7 @@ set(SHADER_FILES
vulkan_blit_depth_stencil.frag vulkan_blit_depth_stencil.frag
vulkan_color_clear.frag vulkan_color_clear.frag
vulkan_color_clear.vert vulkan_color_clear.vert
vulkan_depthstencil_clear.frag
vulkan_fidelityfx_fsr_easu_fp16.comp vulkan_fidelityfx_fsr_easu_fp16.comp
vulkan_fidelityfx_fsr_easu_fp32.comp vulkan_fidelityfx_fsr_easu_fp32.comp
vulkan_fidelityfx_fsr_rcas_fp16.comp vulkan_fidelityfx_fsr_rcas_fp16.comp

View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#version 460 core
layout (push_constant) uniform PushConstants {
vec4 clear_depth;
};
void main() {
gl_FragDepth = clear_depth.x;
}

View File

@@ -220,7 +220,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
ASSERT(num_textures <= MAX_TEXTURES); ASSERT(num_textures <= MAX_TEXTURES);
ASSERT(num_images <= MAX_IMAGES); ASSERT(num_images <= MAX_IMAGES);
const bool assembly_shaders{assembly_programs[0].handle != 0}; const auto backend = device.GetShaderBackend();
const bool assembly_shaders{backend == Settings::ShaderBackend::Glasm};
use_storage_buffers = use_storage_buffers =
!assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); !assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
writes_global_memory &= !use_storage_buffers; writes_global_memory &= !use_storage_buffers;
@@ -230,7 +231,6 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
GenerateTransformFeedbackState(); GenerateTransformFeedbackState();
} }
const bool in_parallel = thread_worker != nullptr; const bool in_parallel = thread_worker != nullptr;
const auto backend = device.GetShaderBackend();
auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv), auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv),
shader_notify, backend, in_parallel, shader_notify, backend, in_parallel,
force_context_flush](ShaderContext::Context*) mutable { force_context_flush](ShaderContext::Context*) mutable {
@@ -559,15 +559,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
} }
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides, glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS);
xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
} }
void GraphicsPipeline::GenerateTransformFeedbackState() { void GraphicsPipeline::GenerateTransformFeedbackState() {
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
// when this is required. // when this is required.
GLint* cursor{xfb_attribs.data()}; GLint* cursor{xfb_attribs.data()};
GLint* current_stream{xfb_streams.data()};
for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
const auto& layout = key.xfb_state.layouts[feedback]; const auto& layout = key.xfb_state.layouts[feedback];
@@ -575,15 +573,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
if (layout.varying_count == 0) { if (layout.varying_count == 0) {
continue; continue;
} }
*current_stream = static_cast<GLint>(feedback);
if (current_stream != xfb_streams.data()) {
// When stepping one stream, push the expected token
cursor[0] = GL_NEXT_BUFFER_NV;
cursor[1] = 0;
cursor[2] = 0;
cursor += XFB_ENTRY_STRIDE;
}
++current_stream;
const auto& locations = key.xfb_state.varyings[feedback]; const auto& locations = key.xfb_state.varyings[feedback];
std::optional<u32> current_index; std::optional<u32> current_index;
@@ -619,7 +608,6 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
} }
} }
num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE); num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE);
num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
} }
void GraphicsPipeline::WaitForBuild() { void GraphicsPipeline::WaitForBuild() {

View File

@@ -154,9 +154,7 @@ private:
static constexpr std::size_t XFB_ENTRY_STRIDE = 3; static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
GLsizei num_xfb_attribs{}; GLsizei num_xfb_attribs{};
GLsizei num_xfb_strides{};
std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
std::mutex built_mutex; std::mutex built_mutex;
std::condition_variable built_condvar; std::condition_variable built_condvar;

View File

@@ -1335,7 +1335,8 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
} }
const u32 buffer_size = static_cast<u32>(buffer_operand.pitch * buffer_operand.height); const u32 buffer_size = static_cast<u32>(buffer_operand.pitch * buffer_operand.height);
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto post_op = IS_IMAGE_UPLOAD ? VideoCommon::ObtainBufferOperation::DoNothing
: VideoCommon::ObtainBufferOperation::MarkAsWritten;
const auto [buffer, offset] = const auto [buffer, offset] =
buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op); buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op);
@@ -1344,8 +1345,12 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
const std::span copy_span{&copy, 1}; const std::span copy_span{&copy, 1};
if constexpr (IS_IMAGE_UPLOAD) { if constexpr (IS_IMAGE_UPLOAD) {
texture_cache.PrepareImage(image_id, true, false);
image->UploadMemory(buffer->Handle(), offset, copy_span); image->UploadMemory(buffer->Handle(), offset, copy_span);
} else { } else {
if (offset % BytesPerBlock(image->info.format)) {
return false;
}
texture_cache.DownloadImageIntoBuffer(image, buffer->Handle(), offset, copy_span, texture_cache.DownloadImageIntoBuffer(image, buffer->Handle(), offset, copy_span,
buffer_operand.address, buffer_size); buffer_operand.address, buffer_size);
} }

View File

@@ -16,6 +16,7 @@
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h" #include "video_core/host_shaders/vulkan_color_clear_frag_spv.h"
#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h" #include "video_core/host_shaders/vulkan_color_clear_vert_spv.h"
#include "video_core/host_shaders/vulkan_depthstencil_clear_frag_spv.h"
#include "video_core/renderer_vulkan/blit_image.h" #include "video_core/renderer_vulkan/blit_image.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -428,6 +429,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)), clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)), clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
clear_stencil_frag(BuildShader(device, VULKAN_DEPTHSTENCIL_CLEAR_FRAG_SPV)),
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
@@ -593,6 +595,28 @@ void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_ma
scheduler.InvalidateState(); scheduler.InvalidateState();
} }
void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool depth_clear,
f32 clear_depth, u8 stencil_mask, u32 stencil_ref,
u32 stencil_compare_mask, const Region2D& dst_region) {
const BlitDepthStencilPipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.depth_clear = depth_clear,
.stencil_mask = stencil_mask,
.stencil_compare_mask = stencil_compare_mask,
.stencil_ref = stencil_ref,
};
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) {
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
BindBlitState(cmdbuf, dst_region);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) { const ImageView& src_image_view) {
const VkPipelineLayout layout = *one_texture_pipeline_layout; const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -820,6 +844,61 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
return *clear_color_pipelines.back(); return *clear_color_pipelines.back();
} }
VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key) {
const auto it = std::ranges::find(clear_stencil_keys, key);
if (it != clear_stencil_keys.end()) {
return *clear_stencil_pipelines[std::distance(clear_stencil_keys.begin(), it)];
}
clear_stencil_keys.push_back(key);
const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag);
const auto stencil = VkStencilOpState{
.failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_KEEP,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = key.stencil_compare_mask,
.writeMask = key.stencil_mask,
.reference = key.stencil_ref,
};
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthTestEnable = VK_FALSE,
.depthWriteEnable = key.depth_clear,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = VK_TRUE,
.front = stencil,
.back = stencil,
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *clear_color_pipeline_layout,
.renderPass = key.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
}));
return *clear_stencil_pipelines.back();
}
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
bool is_target_depth) { bool is_target_depth) {
if (pipeline) { if (pipeline) {

View File

@@ -27,6 +27,16 @@ struct BlitImagePipelineKey {
Tegra::Engines::Fermi2D::Operation operation; Tegra::Engines::Fermi2D::Operation operation;
}; };
struct BlitDepthStencilPipelineKey {
constexpr auto operator<=>(const BlitDepthStencilPipelineKey&) const noexcept = default;
VkRenderPass renderpass;
bool depth_clear;
u8 stencil_mask;
u32 stencil_compare_mask;
u32 stencil_ref;
};
class BlitImageHelper { class BlitImageHelper {
public: public:
explicit BlitImageHelper(const Device& device, Scheduler& scheduler, explicit BlitImageHelper(const Device& device, Scheduler& scheduler,
@@ -64,6 +74,10 @@ public:
void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask, void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
const std::array<f32, 4>& clear_color, const Region2D& dst_region); const std::array<f32, 4>& clear_color, const Region2D& dst_region);
void ClearDepthStencil(const Framebuffer* dst_framebuffer, bool depth_clear, f32 clear_depth,
u8 stencil_mask, u32 stencil_ref, u32 stencil_compare_mask,
const Region2D& dst_region);
private: private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view); const ImageView& src_image_view);
@@ -76,6 +90,8 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key); [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
@@ -108,6 +124,7 @@ private:
vk::ShaderModule blit_depth_stencil_frag; vk::ShaderModule blit_depth_stencil_frag;
vk::ShaderModule clear_color_vert; vk::ShaderModule clear_color_vert;
vk::ShaderModule clear_color_frag; vk::ShaderModule clear_color_frag;
vk::ShaderModule clear_stencil_frag;
vk::ShaderModule convert_depth_to_float_frag; vk::ShaderModule convert_depth_to_float_frag;
vk::ShaderModule convert_float_to_depth_frag; vk::ShaderModule convert_float_to_depth_frag;
vk::ShaderModule convert_abgr8_to_d24s8_frag; vk::ShaderModule convert_abgr8_to_d24s8_frag;
@@ -122,6 +139,8 @@ private:
std::vector<vk::Pipeline> blit_depth_stencil_pipelines; std::vector<vk::Pipeline> blit_depth_stencil_pipelines;
std::vector<BlitImagePipelineKey> clear_color_keys; std::vector<BlitImagePipelineKey> clear_color_keys;
std::vector<vk::Pipeline> clear_color_pipelines; std::vector<vk::Pipeline> clear_color_pipelines;
std::vector<BlitDepthStencilPipelineKey> clear_stencil_keys;
std::vector<vk::Pipeline> clear_stencil_pipelines;
vk::Pipeline convert_d32_to_r32_pipeline; vk::Pipeline convert_d32_to_r32_pipeline;
vk::Pipeline convert_r32_to_d32_pipeline; vk::Pipeline convert_r32_to_d32_pipeline;
vk::Pipeline convert_d16_to_r16_pipeline; vk::Pipeline convert_d16_to_r16_pipeline;

View File

@@ -126,7 +126,7 @@ struct FormatTuple {
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1R5G5B5_UNORM
{VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
{VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
{VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // A2R10G10B10_UNORM {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable}, // A2R10G10B10_UNORM
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
{VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled) {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
{VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM

View File

@@ -428,6 +428,17 @@ void RasterizerVulkan::Clear(u32 layer_count) {
if (aspect_flags == 0) { if (aspect_flags == 0) {
return; return;
} }
if (use_stencil && regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0) {
Region2D dst_region = {
Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
Offset2D{.x = clear_rect.rect.offset.x + static_cast<s32>(clear_rect.rect.extent.width),
.y = clear_rect.rect.offset.y +
static_cast<s32>(clear_rect.rect.extent.height)}};
blit_image.ClearDepthStencil(framebuffer, use_depth, regs.clear_depth,
static_cast<u8>(regs.stencil_front_mask), regs.clear_stencil,
regs.stencil_front_func_mask, dst_region);
} else {
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
VkClearAttachment attachment; VkClearAttachment attachment;
@@ -437,6 +448,7 @@ void RasterizerVulkan::Clear(u32 layer_count) {
attachment.clearValue.depthStencil.stencil = clear_stencil; attachment.clearValue.depthStencil.stencil = clear_stencil;
cmdbuf.ClearAttachments(attachment, clear_rect); cmdbuf.ClearAttachments(attachment, clear_rect);
}); });
}
} }
void RasterizerVulkan::DispatchCompute() { void RasterizerVulkan::DispatchCompute() {
@@ -830,7 +842,8 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
} }
const u32 buffer_size = static_cast<u32>(buffer_operand.pitch * buffer_operand.height); const u32 buffer_size = static_cast<u32>(buffer_operand.pitch * buffer_operand.height);
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto post_op = IS_IMAGE_UPLOAD ? VideoCommon::ObtainBufferOperation::DoNothing
: VideoCommon::ObtainBufferOperation::MarkAsWritten;
const auto [buffer, offset] = const auto [buffer, offset] =
buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op); buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op);
@@ -839,8 +852,12 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
const std::span copy_span{&copy, 1}; const std::span copy_span{&copy, 1};
if constexpr (IS_IMAGE_UPLOAD) { if constexpr (IS_IMAGE_UPLOAD) {
texture_cache.PrepareImage(image_id, true, false);
image->UploadMemory(buffer->Handle(), offset, copy_span); image->UploadMemory(buffer->Handle(), offset, copy_span);
} else { } else {
if (offset % BytesPerBlock(image->info.format)) {
return false;
}
texture_cache.DownloadImageIntoBuffer(image, buffer->Handle(), offset, copy_span, texture_cache.DownloadImageIntoBuffer(image, buffer->Handle(), offset, copy_span,
buffer_operand.address, buffer_size); buffer_operand.address, buffer_size);
} }

View File

@@ -243,6 +243,9 @@ public:
/// Create channel state. /// Create channel state.
void CreateChannel(Tegra::Control::ChannelState& channel) final override; void CreateChannel(Tegra::Control::ChannelState& channel) final override;
/// Prepare an image to be used
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
std::recursive_mutex mutex; std::recursive_mutex mutex;
private: private:
@@ -387,9 +390,6 @@ private:
/// Synchronize image aliases, copying data if needed /// Synchronize image aliases, copying data if needed
void SynchronizeAliases(ImageId image_id); void SynchronizeAliases(ImageId image_id);
/// Prepare an image to be used
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
/// Prepare an image view to be used /// Prepare an image view to be used
void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate); void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);

View File

@@ -71,6 +71,11 @@ constexpr std::array R8G8B8_SSCALED{
VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED,
}; };
constexpr std::array VK_FORMAT_R32G32B32_SFLOAT{
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_FORMAT_UNDEFINED,
};
} // namespace Alternatives } // namespace Alternatives
enum class NvidiaArchitecture { enum class NvidiaArchitecture {
@@ -103,6 +108,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
return Alternatives::R16G16B16_SSCALED.data(); return Alternatives::R16G16B16_SSCALED.data();
case VK_FORMAT_R8G8B8_SSCALED: case VK_FORMAT_R8G8B8_SSCALED:
return Alternatives::R8G8B8_SSCALED.data(); return Alternatives::R8G8B8_SSCALED.data();
case VK_FORMAT_R32G32B32_SFLOAT:
return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
default: default:
return nullptr; return nullptr;
} }
@@ -130,6 +137,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
VK_FORMAT_A2B10G10R10_UINT_PACK32, VK_FORMAT_A2B10G10R10_UINT_PACK32,
VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_FORMAT_A2B10G10R10_USCALED_PACK32, VK_FORMAT_A2B10G10R10_USCALED_PACK32,
VK_FORMAT_A2R10G10B10_UNORM_PACK32,
VK_FORMAT_A8B8G8R8_SINT_PACK32, VK_FORMAT_A8B8G8R8_SINT_PACK32,
VK_FORMAT_A8B8G8R8_SNORM_PACK32, VK_FORMAT_A8B8G8R8_SNORM_PACK32,
VK_FORMAT_A8B8G8R8_SRGB_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32,
@@ -326,6 +334,43 @@ std::vector<const char*> ExtensionListForVulkan(
} // Anonymous namespace } // Anonymous namespace
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
extension = false;
loaded_extensions.erase(extension_name);
}
void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
this->RemoveExtension(is_suitable, extension_name);
}
}
template <typename Feature>
void Device::RemoveExtensionFeature(bool& extension, Feature& feature,
const std::string& extension_name) {
// Unload extension.
this->RemoveExtension(extension, extension_name);
// Save sType and pNext for chain.
VkStructureType sType = feature.sType;
void* pNext = feature.pNext;
// Clear feature struct and restore chain.
feature = {};
feature.sType = sType;
feature.pNext = pNext;
}
template <typename Feature>
void Device::RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing features for unsuitable extension {}", extension_name);
this->RemoveExtensionFeature(is_suitable, feature, extension_name);
}
}
Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
const vk::InstanceDispatch& dld_) const vk::InstanceDispatch& dld_)
: instance{instance_}, dld{dld_}, physical{physical_}, : instance{instance_}, dld{dld_}, physical{physical_},
@@ -397,21 +442,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (is_qualcomm || is_turnip) { if (is_qualcomm || is_turnip) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
extensions.custom_border_color = false; RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
} }
if (is_qualcomm) { if (is_qualcomm) {
must_emulate_scaled_formats = true; must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state"); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
#if defined(ANDROID) && defined(ARCHITECTURE_arm64) #if defined(ANDROID) && defined(ARCHITECTURE_arm64)
// Patch the driver to enable BCn textures. // Patch the driver to enable BCn textures.
@@ -440,15 +484,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
must_emulate_scaled_formats = true; must_emulate_scaled_formats = true;
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
if (is_nvidia) { if (is_nvidia) {
@@ -464,8 +505,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
case NvidiaArchitecture::VoltaOrOlder: case NvidiaArchitecture::VoltaOrOlder:
if (nv_major_version < 527) { if (nv_major_version < 527) {
LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
break; break;
} }
@@ -480,8 +520,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state,
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_radv) { if (extensions.extended_dynamic_state2 && is_radv) {
@@ -490,11 +531,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING( LOG_WARNING(
Render_Vulkan, Render_Vulkan,
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_qualcomm) { if (extensions.extended_dynamic_state2 && is_qualcomm) {
@@ -504,11 +543,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false; RemoveExtensionFeature(extensions.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; features.extended_dynamic_state2,
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
extensions.extended_dynamic_state2 = false;
loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state3 && is_radv) { if (extensions.extended_dynamic_state3 && is_radv) {
@@ -540,9 +577,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (is_rdna2) { if (is_rdna2) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
features.vertex_input_dynamic_state.vertexInputDynamicState = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
extensions.vertex_input_dynamic_state = false; features.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.vertex_input_dynamic_state && is_qualcomm) { if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@@ -553,9 +590,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING( LOG_WARNING(
Render_Vulkan, Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
extensions.vertex_input_dynamic_state = false; features.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
@@ -575,8 +612,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (!features.shader_float16_int8.shaderFloat16) { if (!features.shader_float16_int8.shaderFloat16) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
extensions.sampler_filter_minmax = false; RemoveExtension(extensions.sampler_filter_minmax,
loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
} }
} }
@@ -584,8 +621,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const u32 version = (properties.properties.driverVersion << 3) >> 3; const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
extensions.vertex_input_dynamic_state = false; RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@@ -612,8 +650,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
extensions.push_descriptor = false; RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
} }
if (is_mvk) { if (is_mvk) {
@@ -1007,33 +1044,28 @@ bool Device::GetSuitability(bool requires_swapchain) {
return suitable; return suitable;
} }
void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
if (loaded_extensions.contains(extension_name) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
loaded_extensions.erase(extension_name);
}
}
void Device::RemoveUnsuitableExtensions() { void Device::RemoveUnsuitableExtensions() {
// VK_EXT_custom_border_color // VK_EXT_custom_border_color
extensions.custom_border_color = features.custom_border_color.customBorderColors && extensions.custom_border_color = features.custom_border_color.customBorderColors &&
features.custom_border_color.customBorderColorWithoutFormat; features.custom_border_color.customBorderColorWithoutFormat;
RemoveExtensionIfUnsuitable(extensions.custom_border_color, RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// VK_EXT_depth_clip_control // VK_EXT_depth_clip_control
extensions.depth_clip_control = features.depth_clip_control.depthClipControl; extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
RemoveExtensionIfUnsuitable(extensions.depth_clip_control, RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state // VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
features.extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state2 // VK_EXT_extended_dynamic_state2
extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state3 // VK_EXT_extended_dynamic_state3
@@ -1048,26 +1080,28 @@ void Device::RemoveUnsuitableExtensions() {
extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_provoking_vertex // VK_EXT_provoking_vertex
extensions.provoking_vertex = extensions.provoking_vertex =
features.provoking_vertex.provokingVertexLast && features.provoking_vertex.provokingVertexLast &&
features.provoking_vertex.transformFeedbackPreservesProvokingVertex; features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
RemoveExtensionIfUnsuitable(extensions.provoking_vertex, RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
// VK_KHR_shader_atomic_int64 // VK_KHR_shader_atomic_int64
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
features.shader_atomic_int64.shaderSharedInt64Atomics; features.shader_atomic_int64.shaderSharedInt64Atomics;
RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
// VK_EXT_shader_demote_to_helper_invocation // VK_EXT_shader_demote_to_helper_invocation
extensions.shader_demote_to_helper_invocation = extensions.shader_demote_to_helper_invocation =
features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
features.shader_demote_to_helper_invocation,
VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
// VK_EXT_subgroup_size_control // VK_EXT_subgroup_size_control
@@ -1075,7 +1109,8 @@ void Device::RemoveUnsuitableExtensions() {
features.subgroup_size_control.subgroupSizeControl && features.subgroup_size_control.subgroupSizeControl &&
properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
features.subgroup_size_control,
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
// VK_EXT_transform_feedback // VK_EXT_transform_feedback
@@ -1086,24 +1121,27 @@ void Device::RemoveUnsuitableExtensions() {
properties.transform_feedback.maxTransformFeedbackBuffers > 0 && properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
properties.transform_feedback.transformFeedbackQueries && properties.transform_feedback.transformFeedbackQueries &&
properties.transform_feedback.transformFeedbackDraw; properties.transform_feedback.transformFeedbackDraw;
RemoveExtensionIfUnsuitable(extensions.transform_feedback, RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
// VK_EXT_vertex_input_dynamic_state // VK_EXT_vertex_input_dynamic_state
extensions.vertex_input_dynamic_state = extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState; features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
// VK_KHR_pipeline_executable_properties // VK_KHR_pipeline_executable_properties
if (Settings::values.renderer_shader_feedback.GetValue()) { if (Settings::values.renderer_shader_feedback.GetValue()) {
extensions.pipeline_executable_properties = extensions.pipeline_executable_properties =
features.pipeline_executable_properties.pipelineExecutableInfo; features.pipeline_executable_properties.pipelineExecutableInfo;
RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
features.pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} else { } else {
extensions.pipeline_executable_properties = false; RemoveExtensionFeature(extensions.pipeline_executable_properties,
loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); features.pipeline_executable_properties,
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} }
// VK_KHR_workgroup_memory_explicit_layout // VK_KHR_workgroup_memory_explicit_layout
@@ -1113,7 +1151,8 @@ void Device::RemoveUnsuitableExtensions() {
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
features.workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
} }

View File

@@ -639,8 +639,17 @@ private:
// Remove extensions which have incomplete feature support. // Remove extensions which have incomplete feature support.
void RemoveUnsuitableExtensions(); void RemoveUnsuitableExtensions();
void RemoveExtension(bool& extension, const std::string& extension_name);
void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name); void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
template <typename Feature>
void RemoveExtensionFeature(bool& extension, Feature& feature,
const std::string& extension_name);
template <typename Feature>
void RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
const std::string& extension_name);
/// Sets up queue families. /// Sets up queue families.
void SetupFamilies(VkSurfaceKHR surface); void SetupFamilies(VkSurfaceKHR surface);

View File

@@ -214,13 +214,17 @@ void GameList::OnTextChanged(const QString& new_text) {
const int children_count = folder->rowCount(); const int children_count = folder->rowCount();
for (int j = 0; j < children_count; ++j) { for (int j = 0; j < children_count; ++j) {
++children_total; ++children_total;
const QStandardItem* child = folder->child(j, 0); const QStandardItem* child = folder->child(j, 0);
const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong();
const QString file_path = const QString file_path =
child->data(GameListItemPath::FullPathRole).toString().toLower(); child->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title = const QString file_title =
child->data(GameListItemPath::TitleRole).toString().toLower(); child->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_program_id = const QString file_program_id =
child->data(GameListItemPath::ProgramIdRole).toString().toLower(); QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
// Only items which filename in combination with its title contains all words // Only items which filename in combination with its title contains all words
// that are in the searchfield will be visible in the gamelist // that are in the searchfield will be visible in the gamelist
@@ -231,7 +235,7 @@ void GameList::OnTextChanged(const QString& new_text) {
file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} +
file_title; file_title;
if (ContainsAllWords(file_name, edit_filter_text) || if (ContainsAllWords(file_name, edit_filter_text) ||
(file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { (file_program_id.count() == 16 && file_program_id.contains(edit_filter_text))) {
tree_view->setRowHidden(j, folder_index, false); tree_view->setRowHidden(j, folder_index, false);
++result_count; ++result_count;
} else { } else {