Compare commits

..

1 Commits

Author SHA1 Message Date
204335230b Android #39 2023-08-14 00:57:19 +00:00
58 changed files with 422 additions and 979 deletions

View File

@ -134,7 +134,7 @@ else()
endif() endif()
# GCC bugs # GCC bugs
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" 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,7 +49,6 @@ 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,19 +5,13 @@ 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>() {
@ -32,7 +26,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), SetupCallback { RecyclerView.ViewHolder(binding.root) {
lateinit var page: SetupPage lateinit var page: SetupPage
init { init {
@ -41,12 +35,6 @@ 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,
@ -74,15 +62,9 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
MaterialButton.ICON_GRAVITY_END MaterialButton.ICON_GRAVITY_END
} }
setOnClickListener { setOnClickListener {
page.buttonAction.invoke(this@SetupPageViewHolder) page.buttonAction.invoke()
} }
} }
} }
override fun onStepCompleted() {
ViewUtils.hideView(binding.buttonAction, 200)
ViewUtils.showView(binding.textConfirmation, 200)
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
}
} }
} }

View File

@ -207,11 +207,8 @@ class SettingsAdapter(
val sliderBinding = DialogSliderBinding.inflate(inflater) val sliderBinding = DialogSliderBinding.inflate(inflater)
textSliderValue = sliderBinding.textValue textSliderValue = sliderBinding.textValue
textSliderValue!!.text = String.format( textSliderValue!!.text = sliderProgress.toString()
context.getString(R.string.value_with_units), sliderBinding.textUnits.text = item.units
sliderProgress.toString(),
item.units
)
sliderBinding.slider.apply { sliderBinding.slider.apply {
valueFrom = item.min.toFloat() valueFrom = item.min.toFloat()
@ -219,11 +216,7 @@ 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 = String.format( textSliderValue!!.text = sliderProgress.toString()
context.getString(R.string.value_with_units),
sliderProgress.toString(),
item.units
)
} }
} }
@ -232,6 +225,10 @@ 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,17 +25,12 @@ 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 val epochTime = setting.value.toLong()
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingDescription.text = dateFormatter.format(zonedTime)
} }
binding.textSettingValue.visibility = View.VISIBLE
val epochTime = setting.value.toLong()
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime)
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -23,9 +23,6 @@ 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,8 +5,6 @@ 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
@ -35,18 +33,4 @@ 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,33 +17,28 @@ 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)
binding.textSettingDescription.visibility = View.VISIBLE } else if (item is SingleChoiceSetting) {
} else { val resMgr = binding.textSettingDescription.context.resources
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.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i] binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
break return
} }
} }
} 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.textSettingValue.text = item.choices[i] binding.textSettingDescription.text = item.choices[i]
break return
} }
} }
} else {
binding.textSettingDescription.visibility = View.GONE
} }
setStyle(setting.isEditable, binding)
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -4,7 +4,6 @@
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
@ -23,14 +22,6 @@ 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,7 +22,6 @@ 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
setStyle(setting.isEditable, binding) binding.switchWidget.isEnabled = setting.isEditable
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {

View File

@ -19,7 +19,6 @@ 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
@ -33,13 +32,10 @@ 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
@ -116,22 +112,14 @@ 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,
{ {
if (NotificationManagerCompat.from(requireContext()) NotificationManagerCompat.from(requireContext())
.areNotificationsEnabled() .areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@ -145,22 +133,12 @@ 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(
@ -172,8 +150,9 @@ class SetupFragment : Fragment() {
true, true,
R.string.add_games, R.string.add_games,
{ {
gamesDirCallback = it mainActivity.getGamesDirectory.launch(
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
)
}, },
true, true,
R.string.add_games_warning, R.string.add_games_warning,
@ -184,11 +163,7 @@ class SetupFragment : Fragment() {
PreferenceManager.getDefaultSharedPreferences( PreferenceManager.getDefaultSharedPreferences(
YuzuApplication.appContext YuzuApplication.appContext
) )
if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) { preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
} }
) )
) )
@ -206,13 +181,6 @@ 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
@ -226,15 +194,15 @@ class SetupFragment : Fragment() {
super.onPageSelected(position) super.onPageSelected(position)
if (position == 1 && previousPosition == 0) { if (position == 1 && previousPosition == 0) {
ViewUtils.showView(binding.buttonNext) showView(binding.buttonNext)
ViewUtils.showView(binding.buttonBack) showView(binding.buttonBack)
} else if (position == 0 && previousPosition == 1) { } else if (position == 0 && previousPosition == 1) {
ViewUtils.hideView(binding.buttonBack) hideView(binding.buttonBack)
ViewUtils.hideView(binding.buttonNext) hideView(binding.buttonNext)
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) { } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
ViewUtils.hideView(binding.buttonNext) hideView(binding.buttonNext)
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) { } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
ViewUtils.showView(binding.buttonNext) showView(binding.buttonNext)
} }
previousPosition = position previousPosition = position
@ -247,8 +215,7 @@ 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) {
val stepState = currentPage.stepCompleted.invoke() if (currentPage.taskCompleted.invoke()) {
if (stepState != StepState.INCOMPLETE) {
pageForward() pageForward()
return@setOnClickListener return@setOnClickListener
} }
@ -297,15 +264,9 @@ 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)
) { ) {
@ -316,27 +277,6 @@ 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)
@ -344,6 +284,33 @@ 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
} }
@ -359,29 +326,15 @@ class SetupFragment : Fragment() {
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
) { _: View, windowInsets: WindowInsetsCompat -> ) { view: 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(
val leftPadding = barInsets.left + cutoutInsets.left barInsets.left + cutoutInsets.left,
val topPadding = barInsets.top + cutoutInsets.top barInsets.top + cutoutInsets.top,
val rightPadding = barInsets.right + cutoutInsets.right barInsets.right + cutoutInsets.right,
val bottomPadding = barInsets.bottom + cutoutInsets.bottom 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,9 +14,6 @@ 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 {
@ -36,8 +33,4 @@ class HomeViewModel : ViewModel() {
} }
_statusBarShadeVisible.value = visible _statusBarShadeVisible.value = visible
} }
fun setShouldPageForward(pageForward: Boolean) {
_shouldPageForward.value = pageForward
}
} }

View File

@ -10,20 +10,10 @@ data class SetupPage(
val buttonIconId: Int, val buttonIconId: Int,
val leftAlignedIcon: Boolean, val leftAlignedIcon: Boolean,
val buttonTextId: Int, val buttonTextId: Int,
val buttonAction: (callback: SetupCallback) -> Unit, val buttonAction: () -> 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 stepCompleted: () -> StepState = { StepState.UNDEFINED } val taskCompleted: () -> Boolean = { true }
) )
interface SetupCallback {
fun onStepCompleted()
}
enum class StepState {
COMPLETE,
INCOMPLETE,
UNDEFINED
}

View File

@ -266,80 +266,73 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory = val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) { if (result == null) {
processGamesDir(result) return@registerForActivityResult
} }
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
Toast.makeText(
applicationContext,
R.string.games_dir_selected,
Toast.LENGTH_LONG
).show()
gamesViewModel.reloadGames(true)
} }
fun processGamesDir(result: Uri) {
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
Toast.makeText(
applicationContext,
R.string.games_dir_selected,
Toast.LENGTH_LONG
).show()
gamesViewModel.reloadGames(true)
}
val getProdKey = val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) { if (result == null) {
processKey(result) return@registerForActivityResult
} }
}
fun processKey(result: Uri): Boolean { if (FileUtil.getExtension(result) != "keys") {
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return false
}
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
applicationContext,
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
return true
} else {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
R.string.invalid_keys_error, R.string.reading_keys_failure,
R.string.install_keys_failure_description, R.string.install_prod_keys_failure_extension_description
R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG) ).show(supportFragmentManager, MessageDialogFragment.TAG)
return false return@registerForActivityResult
}
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
applicationContext,
R.string.install_keys_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
} else {
MessageDialogFragment.newInstance(
R.string.invalid_keys_error,
R.string.install_keys_failure_description,
R.string.dumping_keys_quickstart_link
).show(supportFragmentManager, MessageDialogFragment.TAG)
}
} }
} }
return false
}
val getFirmware = val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->

View File

@ -1,35 +0,0 @@
// 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"?>
<RelativeLayout <androidx.constraintlayout.widget.ConstraintLayout
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,39 +8,33 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_alignParentTop="true" app:layout_constraintBottom_toBottomOf="parent"
android:layout_alignParentBottom="true" app:layout_constraintEnd_toEndOf="parent"
android:clipToPadding="false" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout <com.google.android.material.button.MaterialButton
android:id="@+id/constraint_buttons" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent" android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_margin="16dp"
android:layout_margin="8dp"> android:text="@string/next"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_next" android:id="@+id/button_back"
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:text="@string/next" android:layout_margin="16dp"
android:visibility="invisible" android:text="@string/back"
app:layout_constraintBottom_toBottomOf="parent" android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/button_back"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -21,76 +21,45 @@
</LinearLayout> </LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
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
android:id="@+id/text_title"
style="@style/TextAppearance.Material3.DisplaySmall" style="@style/TextAppearance.Material3.DisplaySmall"
android:layout_width="0dp" android:id="@+id/text_title"
android:layout_height="0dp" android:layout_width="match_parent"
android:gravity="center" android:layout_height="wrap_content"
android:textAlignment="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"
style="@style/TextAppearance.Material3.TitleLarge" android:layout_width="match_parent"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="0dp" android:layout_marginTop="16dp"
android:gravity="center" android:paddingHorizontal="32dp"
android:textSize="20sp" android:textAlignment="center"
android:paddingHorizontal="16dp" android:textSize="26sp"
app:layout_constraintBottom_toTopOf="@+id/button_action" app:lineHeight="40sp"
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="16dp" android:layout_marginTop="32dp"
android:layout_marginBottom="48dp"
android:textSize="20sp" android:textSize="20sp"
app:iconGravity="end"
app:iconSize="24sp" app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent" app:iconGravity="end"
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" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -5,16 +5,23 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView <TextView
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"?>
<RelativeLayout <androidx.constraintlayout.widget.ConstraintLayout
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,39 +8,35 @@
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2" android:id="@+id/viewPager2"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_next"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@+id/constraint_buttons" android:layout_margin="12dp"
android:layout_alignParentTop="true" android:text="@string/next"
android:clipToPadding="false" /> android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout <com.google.android.material.button.MaterialButton
android:id="@+id/constraint_buttons" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="match_parent" android:id="@+id/button_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="12dp"
android:layout_alignParentBottom="true"> android:text="@string/back"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/button_next"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_back"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <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: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"
@ -12,40 +11,31 @@
android:minHeight="72dp" android:minHeight="72dp"
android:padding="@dimen/spacing_large"> android:padding="@dimen/spacing_large">
<LinearLayout <com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent" style="@style/TextAppearance.Material3.HeadlineMedium"
android:id="@+id/text_setting_name"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:textSize="16sp"
android:textAlignment="viewStart"
app:lineHeight="28dp"
tools:text="Setting Name" />
<com.google.android.material.textview.MaterialTextView <TextView
android:id="@+id/text_setting_name" style="@style/TextAppearance.Material3.BodySmall"
style="@style/TextAppearance.Material3.HeadlineMedium" android:id="@+id/text_setting_description"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:layout_alignParentEnd="true"
android:textSize="16sp" android:layout_alignParentStart="true"
app:lineHeight="22dp" android:layout_alignStart="@+id/text_setting_name"
tools:text="Setting Name" /> android:layout_below="@+id/text_setting_name"
android:layout_marginTop="@dimen/spacing_small"
<com.google.android.material.textview.MaterialTextView android:visibility="visible"
android:id="@+id/text_setting_description" android:textAlignment="viewStart"
style="@style/TextAppearance.Material3.BodySmall" tools:text="@string/app_disclaimer" />
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
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,12 +21,11 @@
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.DisplaySmall" style="@style/TextAppearance.Material3.DisplayMedium"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
@ -45,42 +44,23 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:textAlignment="center" android:textAlignment="center"
android:textSize="20sp" android:textSize="26sp"
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="30sp" app:lineHeight="40sp"
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,7 +29,6 @@
<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>
@ -150,7 +149,6 @@
<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

@ -263,23 +263,6 @@ 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,19 +289,6 @@ 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 {
@ -347,14 +334,6 @@ 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{};
@ -683,11 +662,6 @@ 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,7 +19,13 @@ public:
void Initialize(); void Initialize();
void Finalize(); void Finalize();
s64 GetTick() const; s64 GetCount() 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};
@ -36,6 +42,7 @@ 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,7 +5,6 @@
#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"
@ -16,7 +15,9 @@ 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() {} void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing) {
m_core_timing = core_timing;
}
void KResourceLimit::Finalize() {} void KResourceLimit::Finalize() {}
@ -85,7 +86,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_kernel.HardwareTimer().GetTick() + DefaultTimeout); return Reserve(which, value, m_core_timing->GetGlobalTimeNs().count() + DefaultTimeout);
} }
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
@ -116,7 +117,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_kernel.HardwareTimer().GetTick() < timeout)) { (timeout < 0 || m_core_timing->GetGlobalTimeNs().count() < 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--;
@ -153,7 +154,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(); resource_limit->Initialize(std::addressof(system.CoreTiming()));
// 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(); void Initialize(const Core::Timing::CoreTiming* core_timing);
void Finalize() override; void Finalize() override;
s64 GetLimitValue(LimitableResource which) const; s64 GetLimitValue(LimitableResource which) const;
@ -57,6 +57,7 @@ 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->RegisterAbsoluteTask(m_thread, m_timeout_tick); m_timer->RegisterTask(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(); system_resource_limit->Initialize(&core_timing);
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,7 +2,6 @@
// 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"
@ -53,7 +52,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 = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2; timeout = offset_tick + 2;
if (timeout <= 0) { if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max(); timeout = std::numeric_limits<s64>::max();
} }

View File

@ -2,7 +2,6 @@
// 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"
@ -26,7 +25,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 = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2; timeout = offset_tick + 2;
if (timeout <= 0) { if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max(); timeout = std::numeric_limits<s64>::max();
} }

View File

@ -5,7 +5,6 @@
#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"
@ -83,29 +82,12 @@ 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); num_handles, timeout_ns);
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(); resource_limit->Initialize(std::addressof(system.CoreTiming()));
// Register the limit. // Register the limit.
KResourceLimit::Register(kernel, resource_limit); KResourceLimit::Register(kernel, resource_limit);

View File

@ -4,7 +4,6 @@
#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"
@ -84,20 +83,9 @@ 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 = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout); Result res =
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,7 +4,6 @@
#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"
@ -43,9 +42,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(std::addressof(process), KScopedResourceReservation thread_reservation(
LimitableResource::ThreadCountMax, 1, std::addressof(process), LimitableResource::ThreadCountMax, 1,
kernel.HardwareTimer().GetTick() + 100000000); system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached); R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached);
// Create the thread. // Create the thread.
@ -103,31 +102,20 @@ void ExitThread(Core::System& system) {
} }
/// Sleep the current thread /// Sleep the current thread
void SleepThread(Core::System& system, s64 ns) { void SleepThread(Core::System& system, s64 nanoseconds) {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
const auto yield_type = static_cast<Svc::YieldType>(ns); const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", ns); LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
// When the input tick is positive, sleep. // When the input tick is positive, sleep.
if (ns > 0) { if (nanoseconds > 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(timeout)); static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
} 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) {
@ -136,6 +124,7 @@ void SleepThread(Core::System& system, s64 ns) {
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,6 +16,22 @@ 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<Core::HID::TouchScreenConfigurationForNx>()}; const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::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,8 +2543,7 @@ public:
class HidSys final : public ServiceFramework<HidSys> { class HidSys final : public ServiceFramework<HidSys> {
public: public:
explicit HidSys(Core::System& system_) explicit HidSys(Core::System& system_) : ServiceFramework{system_, "hid:sys"} {
: 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"},
@ -2569,7 +2568,7 @@ public:
{303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, nullptr, "EnableAssigningSingleOnSlSrPress"}, {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
{305, nullptr, "DisableAssigningSingleOnSlSrPress"}, {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
{306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"}, {306, nullptr, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"}, {307, nullptr, "GetNpadSystemExtStyle"},
{308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"}, {309, nullptr, "GetNpadFullKeyGripColor"},
@ -2625,7 +2624,7 @@ public:
{700, nullptr, "ActivateUniquePad"}, {700, nullptr, "ActivateUniquePad"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"}, {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"}, {703, nullptr, "GetUniquePadIds"},
{751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, {751, nullptr, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"}, {800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
{802, nullptr, "ResetSixAxisSensorCalibrationValues"}, {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
@ -2651,7 +2650,7 @@ public:
{830, nullptr, "SetNotificationLedPattern"}, {830, nullptr, "SetNotificationLedPattern"},
{831, nullptr, "SetNotificationLedPatternWithTimeout"}, {831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"}, {832, nullptr, "PrepareHidsForNotificationWake"},
{850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, {850, nullptr, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"}, {851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"}, {852, nullptr, "IsUsbConnected"},
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
@ -2683,7 +2682,7 @@ public:
{1150, nullptr, "SetTouchScreenMagnification"}, {1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"}, {1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, nullptr, "SetTouchScreenDefaultConfiguration"}, {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
{1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, {1153, nullptr, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"}, {1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"}, {1155, nullptr, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
@ -2750,8 +2749,6 @@ public:
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
} }
private: private:
@ -2763,66 +2760,17 @@ 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(static_cast<u32>(unique_pads.size())); rb.Push(total_entries);
} }
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,7 +449,6 @@ 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,7 +41,6 @@ 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,9 +18,7 @@ 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,14 +23,10 @@ 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); return HandleReturn("SSLHandshake", 0, status).Code();
} }
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,11 +39,6 @@ namespace Network {
namespace { namespace {
enum class CallType {
Send,
Other,
};
#ifdef _WIN32 #ifdef _WIN32
using socklen_t = int; using socklen_t = int;
@ -101,7 +96,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, CallType call_type = CallType::Other) { Errno TranslateNativeError(int e) {
switch (e) { switch (e) {
case 0: case 0:
return Errno::SUCCESS; return Errno::SUCCESS;
@ -117,14 +112,6 @@ Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
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:
@ -211,7 +198,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, CallType call_type = CallType::Other) { Errno TranslateNativeError(int e) {
switch (e) { switch (e) {
case 0: case 0:
return Errno::SUCCESS; return Errno::SUCCESS;
@ -221,10 +208,6 @@ Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
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:
@ -253,13 +236,13 @@ Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
#endif #endif
Errno GetAndLogLastError(CallType call_type = CallType::Other) { Errno GetAndLogLastError() {
#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, call_type); const Errno err = TranslateNativeError(e);
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));
@ -493,13 +476,7 @@ 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()) {
// Only print the error once to avoid log spam LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface");
static bool print_error = true;
if (print_error) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
print_error = false;
}
return {}; return {};
} }
@ -748,17 +725,13 @@ 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()), native_flags); static_cast<int>(message.size()), 0);
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(CallType::Send)}; return {-1, GetAndLogLastError()};
} }
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,
@ -780,7 +753,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(CallType::Send)}; return {-1, GetAndLogLastError()};
} }
Errno Socket::Close() { Errno Socket::Close() {

View File

@ -33,12 +33,10 @@ 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,14 +200,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
}); });
if (res == network_interfaces.end()) { if (res == network_interfaces.end()) {
// Only print the error once to avoid log spam LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
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

@ -50,7 +50,6 @@ 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

@ -1,12 +0,0 @@
// 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,8 +220,7 @@ 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 auto backend = device.GetShaderBackend(); const bool assembly_shaders{assembly_programs[0].handle != 0};
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;
@ -231,6 +230,7 @@ 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,13 +559,15 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
} }
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides,
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];
@ -573,6 +575,15 @@ 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;
@ -608,6 +619,7 @@ 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,7 +154,9 @@ 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,8 +1335,7 @@ 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 = IS_IMAGE_UPLOAD ? VideoCommon::ObtainBufferOperation::DoNothing const auto post_op = 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);
@ -1345,12 +1344,8 @@ 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,7 +16,6 @@
#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"
@ -429,7 +428,6 @@ 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)),
@ -595,28 +593,6 @@ 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;
@ -844,61 +820,6 @@ 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,16 +27,6 @@ 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,
@ -74,10 +64,6 @@ 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);
@ -90,8 +76,6 @@ 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);
@ -124,7 +108,6 @@ 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;
@ -139,8 +122,6 @@ 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}, // A2R10G10B10_UNORM {VK_FORMAT_A2R10G10B10_UNORM_PACK32, Attachable | Storage}, // 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,27 +428,15 @@ void RasterizerVulkan::Clear(u32 layer_count) {
if (aspect_flags == 0) { if (aspect_flags == 0) {
return; return;
} }
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
if (use_stencil && regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0) { clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
Region2D dst_region = { VkClearAttachment attachment;
Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y}, attachment.aspectMask = aspect_flags;
Offset2D{.x = clear_rect.rect.offset.x + static_cast<s32>(clear_rect.rect.extent.width), attachment.colorAttachment = 0;
.y = clear_rect.rect.offset.y + attachment.clearValue.depthStencil.depth = clear_depth;
static_cast<s32>(clear_rect.rect.extent.height)}}; attachment.clearValue.depthStencil.stencil = clear_stencil;
blit_image.ClearDepthStencil(framebuffer, use_depth, regs.clear_depth, cmdbuf.ClearAttachments(attachment, clear_rect);
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,
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
VkClearAttachment attachment;
attachment.aspectMask = aspect_flags;
attachment.colorAttachment = 0;
attachment.clearValue.depthStencil.depth = clear_depth;
attachment.clearValue.depthStencil.stencil = clear_stencil;
cmdbuf.ClearAttachments(attachment, clear_rect);
});
}
} }
void RasterizerVulkan::DispatchCompute() { void RasterizerVulkan::DispatchCompute() {
@ -842,8 +830,7 @@ 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 = IS_IMAGE_UPLOAD ? VideoCommon::ObtainBufferOperation::DoNothing const auto post_op = 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);
@ -852,12 +839,8 @@ 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,9 +243,6 @@ 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:
@ -390,6 +387,9 @@ 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,11 +71,6 @@ 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 {
@ -108,8 +103,6 @@ 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;
} }
@ -137,7 +130,6 @@ 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,
@ -334,43 +326,6 @@ 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_},
@ -442,20 +397,21 @@ 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");
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, extensions.custom_border_color = false;
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); loaded_extensions.erase(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");
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, extensions.extended_dynamic_state = false;
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); loaded_extensions.erase(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");
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); extensions.push_descriptor = false;
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.
@ -484,12 +440,15 @@ 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");
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, extensions.extended_dynamic_state = false;
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); loaded_extensions.erase(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");
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, features.extended_dynamic_state2.extendedDynamicState2 = false;
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
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) {
@ -505,7 +464,8 @@ 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");
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); extensions.push_descriptor = false;
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
break; break;
} }
@ -520,9 +480,8 @@ 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");
RemoveExtensionFeature(extensions.extended_dynamic_state, extensions.extended_dynamic_state = false;
features.extended_dynamic_state, loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.extended_dynamic_state2 && is_radv) { if (extensions.extended_dynamic_state2 && is_radv) {
@ -531,9 +490,11 @@ 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");
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2.extendedDynamicState2 = false;
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 (extensions.extended_dynamic_state2 && is_qualcomm) { if (extensions.extended_dynamic_state2 && is_qualcomm) {
@ -543,9 +504,11 @@ 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");
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2.extendedDynamicState2 = false;
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 (extensions.extended_dynamic_state3 && is_radv) { if (extensions.extended_dynamic_state3 && is_radv) {
@ -577,9 +540,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");
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state.vertexInputDynamicState = false;
features.vertex_input_dynamic_state, extensions.vertex_input_dynamic_state = false;
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
if (extensions.vertex_input_dynamic_state && is_qualcomm) { if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@ -590,9 +553,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");
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state.vertexInputDynamicState = false;
features.vertex_input_dynamic_state, extensions.vertex_input_dynamic_state = false;
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} }
} }
@ -612,8 +575,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");
RemoveExtension(extensions.sampler_filter_minmax, extensions.sampler_filter_minmax = false;
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
} }
} }
@ -621,9 +584,8 @@ 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");
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 (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@ -650,7 +612,8 @@ 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");
RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); extensions.push_descriptor = false;
loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
} }
} }
if (is_mvk) { if (is_mvk) {
@ -1044,29 +1007,34 @@ 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;
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, RemoveExtensionIfUnsuitable(extensions.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;
RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control, RemoveExtensionIfUnsuitable(extensions.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;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state, RemoveExtensionIfUnsuitable(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;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2, RemoveExtensionIfUnsuitable(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
dynamic_state3_blending = dynamic_state3_blending =
@ -1080,38 +1048,35 @@ 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;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3, RemoveExtensionIfUnsuitable(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;
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex, RemoveExtensionIfUnsuitable(extensions.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;
RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64, RemoveExtensionIfUnsuitable(extensions.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;
RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation, RemoveExtensionIfUnsuitable(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
extensions.subgroup_size_control = extensions.subgroup_size_control =
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;
RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control, RemoveExtensionIfUnsuitable(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
extensions.transform_feedback = extensions.transform_feedback =
@ -1121,27 +1086,24 @@ 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;
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, RemoveExtensionIfUnsuitable(extensions.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;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state, RemoveExtensionIfUnsuitable(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;
RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties, RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties,
features.pipeline_executable_properties, VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} else { } else {
RemoveExtensionFeature(extensions.pipeline_executable_properties, extensions.pipeline_executable_properties = false;
features.pipeline_executable_properties, loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
} }
// VK_KHR_workgroup_memory_explicit_layout // VK_KHR_workgroup_memory_explicit_layout
@ -1151,9 +1113,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;
RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout, RemoveExtensionIfUnsuitable(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);
} }
void Device::SetupFamilies(VkSurfaceKHR surface) { void Device::SetupFamilies(VkSurfaceKHR surface) {

View File

@ -639,17 +639,8 @@ 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);