Compare commits

..

1 Commits

Author SHA1 Message Date
28a41827f3 Android #78 2023-09-22 00:57:37 +00:00
121 changed files with 1127 additions and 5300 deletions

View File

@ -247,12 +247,7 @@ object NativeLibrary {
external fun setAppDirectory(directory: String) external fun setAppDirectory(directory: String)
/** external fun installFileToNand(filename: String): Int
* Installs a nsp or xci file to nand
* @param filename String representation of file uri
* @param extension Lowercase string representation of file extension without "."
*/
external fun installFileToNand(filename: String, extension: String): Int
external fun initializeGpuDriver( external fun initializeGpuDriver(
hookLibDir: String?, hookLibDir: String?,
@ -516,11 +511,6 @@ object NativeLibrary {
*/ */
external fun submitInlineKeyboardInput(key_code: Int) external fun submitInlineKeyboardInput(key_code: Int)
/**
* Creates a generic user directory if it doesn't exist already
*/
external fun initializeEmptyUserDirectory()
/** /**
* Button type for use in onTouchEvent * Button type for use in onTouchEvent
*/ */

View File

@ -1,49 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable
class InstallableAdapter(private val installables: List<Installable>) :
RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): InstallableAdapter.InstallableViewHolder {
val binding =
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return InstallableViewHolder(binding)
}
override fun getItemCount(): Int = installables.size
override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
holder.bind(installables[position])
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var installable: Installable
fun bind(installable: Installable) {
this.installable = installable
binding.title.setText(installable.titleId)
binding.description.setText(installable.descriptionId)
if (installable.install != null) {
binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { installable.install.invoke() }
}
if (installable.export != null) {
binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { installable.export.invoke() }
}
}
}
}

View File

@ -21,7 +21,6 @@ import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException import java.io.IOException
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@ -169,7 +168,7 @@ class SettingsActivity : AppCompatActivity() {
if (!settingsFile.delete()) { if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile") throw IOException("Failed to delete $settingsFile")
} }
NativeLibrary.reloadSettings() Settings.settingsList.forEach { it.reset() }
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,
@ -182,14 +181,12 @@ class SettingsActivity : AppCompatActivity() {
private fun setInsets() { private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.navigationBarShade binding.navigationBarShade
) { _: View, windowInsets: WindowInsetsCompat -> ) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
// The only situation where we care to have a nav bar shade is when it's at the bottom val mlpShade = view.layoutParams as MarginLayoutParams
// of the screen where scrolling list elements can go behind it. mlpShade.height = barInsets.bottom
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams view.layoutParams = mlpShade
mlpNavShade.height = barInsets.bottom
binding.navigationBarShade.layoutParams = mlpNavShade
windowInsets windowInsets
} }

View File

@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
class AboutFragment : Fragment() { class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null private var _binding: FragmentAboutBinding? = null
@ -92,6 +93,12 @@ class AboutFragment : Fragment() {
} }
} }
val mainActivity = requireActivity() as MainActivity
binding.buttonExport.setOnClickListener { mainActivity.exportUserData.launch("export.zip") }
binding.buttonImport.setOnClickListener {
mainActivity.importUserData.launch(arrayOf("application/zip"))
}
binding.buttonDiscord.setOnClickListener { openLink(getString(R.string.support_link)) } binding.buttonDiscord.setOnClickListener { openLink(getString(R.string.support_link)) }
binding.buttonWebsite.setOnClickListener { openLink(getString(R.string.website_link)) } binding.buttonWebsite.setOnClickListener { openLink(getString(R.string.website_link)) }
binding.buttonGithub.setOnClickListener { openLink(getString(R.string.github_link)) } binding.buttonGithub.setOnClickListener { openLink(getString(R.string.github_link)) }

View File

@ -17,7 +17,6 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.* import android.view.*
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -54,7 +53,6 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback { class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
@ -106,21 +104,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
null null
} }
} }
game = if (args.game != null) {
try { args.game!!
game = if (args.game != null) { } else {
args.game!! intentGame ?: error("[EmulationFragment] No bootable game present!")
} else {
intentGame!!
}
} catch (e: NullPointerException) {
Toast.makeText(
requireContext(),
R.string.no_game_present,
Toast.LENGTH_SHORT
).show()
requireActivity().finish()
return
} }
// So this fragment doesn't restart on configuration changes; i.e. rotation. // So this fragment doesn't restart on configuration changes; i.e. rotation.
@ -144,11 +131,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// This is using the correct scope, lint is just acting up // This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector") @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (requireActivity().isFinishing) {
return
}
binding.surfaceEmulation.holder.addCallback(this) binding.surfaceEmulation.holder.addCallback(this)
binding.showFpsText.setTextColor(Color.YELLOW) binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
@ -304,23 +286,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
if (_binding == null) {
return
}
updateScreenLayout() updateScreenLayout()
if (emulationActivity?.isInPictureInPictureMode == true) { if (emulationActivity?.isInPictureInPictureMode == true) {
if (binding.drawerLayout.isOpen) { if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close() binding.drawerLayout.close()
} }
if (EmulationMenuSettings.showOverlay) { if (EmulationMenuSettings.showOverlay) {
binding.surfaceInputOverlay.visibility = View.INVISIBLE binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
} }
} else { } else {
if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) { if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.visibility = View.VISIBLE binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.visibility = View.VISIBLE
}
} else { } else {
binding.surfaceInputOverlay.visibility = View.INVISIBLE binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
} }
if (!isInFoldableLayout) { if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

View File

@ -118,13 +118,18 @@ class HomeSettingsFragment : Fragment() {
) )
add( add(
HomeSetting( HomeSetting(
R.string.manage_yuzu_data, R.string.install_amiibo_keys,
R.string.manage_yuzu_data_description, R.string.install_amiibo_keys_description,
R.drawable.ic_install, R.drawable.ic_nfc,
{ { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
binding.root.findNavController() )
.navigate(R.id.action_homeSettingsFragment_to_installableFragment) )
} add(
HomeSetting(
R.string.install_game_content,
R.string.install_game_content_description,
R.drawable.ic_system_update_alt,
{ mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
) )
) )
add( add(
@ -143,6 +148,35 @@ class HomeSettingsFragment : Fragment() {
homeViewModel.gamesDir homeViewModel.gamesDir
) )
) )
add(
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
R.drawable.ic_save,
{
ImportExportSavesFragment().show(
parentFragmentManager,
ImportExportSavesFragment.TAG
)
}
)
)
add(
HomeSetting(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
R.drawable.ic_unlock,
{ mainActivity.getProdKey.launch(arrayOf("*/*")) }
)
)
add(
HomeSetting(
R.string.install_firmware,
R.string.install_firmware_description,
R.drawable.ic_firmware,
{ mainActivity.getFirmware.launch(arrayOf("application/zip")) }
)
)
add( add(
HomeSetting( HomeSetting(
R.string.share_log, R.string.share_log,

View File

@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FilenameFilter
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.getPublicFilesDir
import org.yuzu.yuzu_emu.utils.FileUtil
class ImportExportSavesFragment : DialogFragment() {
private val context = YuzuApplication.appContext
private val savesFolder =
"${context.getPublicFilesDir().canonicalPath}/nand/user/save/0000000000000000"
// Get first subfolder in saves folder (should be the user folder)
private val savesFolderRoot = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
private var lastZipCreated: File? = null
private lateinit var startForResultExportSave: ActivityResultLauncher<Intent>
private lateinit var documentPicker: ActivityResultLauncher<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activity = requireActivity() as AppCompatActivity
val activityResultRegistry = requireActivity().activityResultRegistry
startForResultExportSave = activityResultRegistry.register(
"startForResultExportSaveKey",
ActivityResultContracts.StartActivityForResult()
) {
File(context.getPublicFilesDir().canonicalPath, "temp").deleteRecursively()
}
documentPicker = activityResultRegistry.register(
"documentPickerKey",
ActivityResultContracts.OpenDocument()
) {
it?.let { uri -> importSave(uri, activity) }
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return if (savesFolderRoot == "") {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.manage_save_data)
.setMessage(R.string.import_export_saves_no_profile)
.setPositiveButton(android.R.string.ok, null)
.show()
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.manage_save_data)
.setMessage(R.string.manage_save_data_description)
.setNegativeButton(R.string.export_saves) { _, _ ->
exportSave()
}
.setPositiveButton(R.string.import_saves) { _, _ ->
documentPicker.launch(arrayOf("application/zip"))
}
.setNeutralButton(android.R.string.cancel, null)
.show()
}
}
/**
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
* @return true if the zip file is successfully created, false otherwise.
*/
private fun zipSave(): Boolean {
try {
val tempFolder = File(requireContext().getPublicFilesDir().canonicalPath, "temp")
tempFolder.mkdirs()
val saveFolder = File(savesFolderRoot)
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
saveFolder.walkTopDown().forEach { file ->
val zipFileName =
file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
if (zipFileName == "") {
return@forEach
}
val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
zos.putNextEntry(entry)
if (file.isFile) {
file.inputStream().use { fis -> fis.copyTo(zos) }
}
}
}
lastZipCreated = outputZipFile
} catch (e: Exception) {
return false
}
return true
}
/**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/
private fun exportSave() {
CoroutineScope(Dispatchers.IO).launch {
val wasZipCreated = zipSave()
val lastZipFile = lastZipCreated
if (!wasZipCreated || lastZipFile == null) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "Failed to export save", Toast.LENGTH_LONG).show()
}
return@launch
}
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
context,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, "application/zip")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startForResultExportSave.launch(Intent.createChooser(intent, "Share save file"))
}
}
}
/**
* Imports the save files contained in the zip file, and replaces any existing ones with the new save file.
* @param zipUri The Uri of the zip file containing the save file(s) to import.
*/
private fun importSave(zipUri: Uri, activity: AppCompatActivity) {
val inputZip = context.contentResolver.openInputStream(zipUri)
// A zip needs to have at least one subfolder named after a TitleId in order to be considered valid.
var validZip = false
val savesFolder = File(savesFolderRoot)
val cacheSaveDir = File("${context.cacheDir.path}/saves/")
cacheSaveDir.mkdir()
if (inputZip == null) {
Toast.makeText(context, context.getString(R.string.fatal_error), Toast.LENGTH_LONG)
.show()
return
}
val filterTitleId =
FilenameFilter { _, dirName -> dirName.matches(Regex("^0100[\\dA-Fa-f]{12}$")) }
try {
CoroutineScope(Dispatchers.IO).launch {
FileUtil.unzip(inputZip, cacheSaveDir)
cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
File(savesFolder, savePath).deleteRecursively()
File(cacheSaveDir, savePath).copyRecursively(File(savesFolder, savePath), true)
validZip = true
}
withContext(Dispatchers.Main) {
if (!validZip) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.save_file_invalid_zip_structure,
descriptionId = R.string.save_file_invalid_zip_structure_description
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
return@withContext
}
Toast.makeText(
context,
context.getString(R.string.save_file_imported_success),
Toast.LENGTH_LONG
).show()
}
cacheSaveDir.deleteRecursively()
}
} catch (e: Exception) {
Toast.makeText(context, context.getString(R.string.fatal_error), Toast.LENGTH_LONG)
.show()
}
}
companion object {
const val TAG = "ImportExportSavesFragment"
}
}

View File

@ -4,12 +4,12 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -39,7 +39,9 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
.setView(binding.root) .setView(binding.root)
if (cancellable) { if (cancellable) {
dialog.setNegativeButton(android.R.string.cancel, null) dialog.setNegativeButton(android.R.string.cancel) { _: DialogInterface, _: Int ->
taskViewModel.setCancelled(true)
}
} }
val alertDialog = dialog.create() val alertDialog = dialog.create()
@ -96,18 +98,6 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
} }
} }
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
// Setting the OnClickListener again after the dialog is shown overrides this behavior.
override fun onResume() {
super.onResume()
val alertDialog = dialog as AlertDialog
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
negativeButton.setOnClickListener {
alertDialog.setTitle(getString(R.string.cancelling))
taskViewModel.setCancelled(true)
}
}
companion object { companion object {
const val TAG = "IndeterminateProgressDialogFragment" const val TAG = "IndeterminateProgressDialogFragment"

View File

@ -1,138 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.ui.main.MainActivity
class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentInstallablesBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mainActivity = requireActivity() as MainActivity
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarInstallables.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
val installables = listOf(
Installable(
R.string.user_data,
R.string.user_data_description,
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
export = { mainActivity.exportUserData.launch("export.zip") }
),
Installable(
R.string.install_game_content,
R.string.install_game_content_description,
install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
),
Installable(
R.string.install_firmware,
R.string.install_firmware_description,
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
),
if (mainActivity.savesFolderRoot != "") {
Installable(
R.string.manage_save_data,
R.string.import_export_saves_description,
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
export = { mainActivity.exportSave() }
)
} else {
Installable(
R.string.manage_save_data,
R.string.import_export_saves_description,
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }
)
},
Installable(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
install = { mainActivity.getProdKey.launch(arrayOf("*/*")) }
),
Installable(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
install = { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
)
)
binding.listInstallables.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
adapter = InstallableAdapter(installables)
}
setInsets()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarInstallables.layoutParams = mlpAppBar
val mlpScrollAbout =
binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listInstallables.layoutParams = mlpScrollAbout
binding.listInstallables.updatePadding(bottom = barInsets.bottom)
windowInsets
}
}

View File

@ -295,10 +295,8 @@ class SetupFragment : Fragment() {
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
if (_binding != null) { outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible) outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
}
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned) outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
} }
@ -355,15 +353,11 @@ class SetupFragment : Fragment() {
} }
fun pageForward() { fun pageForward() {
if (_binding != null) { binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
binding.viewPager2.currentItem += 1
}
} }
fun pageBackward() { fun pageBackward() {
if (_binding != null) { binding.viewPager2.currentItem = binding.viewPager2.currentItem - 1
binding.viewPager2.currentItem -= 1
}
} }
fun setPageWarned(page: Int) { fun setPageWarned(page: Int) {

View File

@ -1,13 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.annotation.StringRes
data class Installable(
@StringRes val titleId: Int,
@StringRes val descriptionId: Int,
val install: (() -> Unit)? = null,
val export: (() -> Unit)? = null
)

View File

@ -50,9 +50,3 @@ class TaskViewModel : ViewModel() {
} }
} }
} }
enum class TaskState {
Completed,
Failed,
Cancelled
}

View File

@ -352,7 +352,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
} }
private fun addOverlayControls(layout: String) { private fun addOverlayControls(layout: String) {
val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) val windowSize = getSafeScreenSize(context)
if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) { if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
overlayButtons.add( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
@ -593,7 +593,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
} }
private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) { private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) val windowSize = getSafeScreenSize(context)
val min = windowSize.first val min = windowSize.first
val max = windowSize.second val max = windowSize.second
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
@ -968,17 +968,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
* @return A pair of points, the first being the top left corner of the safe area, * @return A pair of points, the first being the top left corner of the safe area,
* the second being the bottom right corner of the safe area * the second being the bottom right corner of the safe area
*/ */
private fun getSafeScreenSize( private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
context: Context,
screenSize: Pair<Int, Int>
): Pair<Point, Point> {
// Get screen size // Get screen size
val windowMetrics = WindowMetricsCalculator.getOrCreate() val windowMetrics = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(context as Activity) .computeCurrentWindowMetrics(context as Activity)
var maxX = screenSize.first.toFloat() var maxY = windowMetrics.bounds.height().toFloat()
var maxY = screenSize.second.toFloat() var maxX = windowMetrics.bounds.width().toFloat()
var minX = 0
var minY = 0 var minY = 0
var minX = 0
// If we have API access, calculate the safe area to draw the overlay // If we have API access, calculate the safe area to draw the overlay
var cutoutLeft = 0 var cutoutLeft = 0

View File

@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.DocumentsContract
import android.view.View import android.view.View
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager import android.view.WindowManager
@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
@ -31,7 +29,6 @@ import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.CoroutineScope
import java.io.File import java.io.File
import java.io.FilenameFilter import java.io.FilenameFilter
import java.io.IOException import java.io.IOException
@ -44,23 +41,19 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.getPublicFilesDir
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
class MainActivity : AppCompatActivity(), ThemeProvider { class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@ -71,13 +64,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override var themeId: Int = 0 override var themeId: Int = 0
private val savesFolder
get() = "${getPublicFilesDir().canonicalPath}/nand/user/save/0000000000000000"
// Get first subfolder in saves folder (should be the user folder)
val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
private var lastZipCreated: File? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@ -395,7 +381,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val task: () -> Any = { val task: () -> Any = {
var messageToShow: Any var messageToShow: Any
try { try {
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheFirmwareDir) FileUtil.unzip(inputZip, cacheFirmwareDir)
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1 val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
@ -528,7 +514,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (documents.isNotEmpty()) { if (documents.isNotEmpty()) {
IndeterminateProgressDialogFragment.newInstance( IndeterminateProgressDialogFragment.newInstance(
this@MainActivity, this@MainActivity,
R.string.installing_game_content R.string.install_game_content
) { ) {
var installSuccess = 0 var installSuccess = 0
var installOverwrite = 0 var installOverwrite = 0
@ -536,12 +522,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
var errorExtension = 0 var errorExtension = 0
var errorOther = 0 var errorOther = 0
documents.forEach { documents.forEach {
when ( when (NativeLibrary.installFileToNand(it.toString())) {
NativeLibrary.installFileToNand(
it.toString(),
FileUtil.getExtension(it)
)
) {
NativeLibrary.InstallFileToNandResult.Success -> { NativeLibrary.InstallFileToNandResult.Success -> {
installSuccess += 1 installSuccess += 1
} }
@ -643,17 +624,27 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
R.string.exporting_user_data, R.string.exporting_user_data,
true true
) { ) {
val zipResult = FileUtil.zipFromInternalStorage( val zos = ZipOutputStream(
File(DirectoryInitialization.userDirectory!!), BufferedOutputStream(contentResolver.openOutputStream(result))
DirectoryInitialization.userDirectory!!,
BufferedOutputStream(contentResolver.openOutputStream(result)),
taskViewModel.cancelled
) )
return@newInstance when (zipResult) { zos.use { stream ->
TaskState.Completed -> getString(R.string.user_data_export_success) File(DirectoryInitialization.userDirectory!!).walkTopDown().forEach { file ->
TaskState.Failed -> R.string.export_failed if (taskViewModel.cancelled.value) {
TaskState.Cancelled -> R.string.user_data_export_cancelled return@newInstance R.string.user_data_export_cancelled
}
if (!file.isDirectory) {
val newPath = file.path.substring(
DirectoryInitialization.userDirectory!!.length,
file.path.length
)
stream.putNextEntry(ZipEntry(newPath))
stream.write(file.readBytes())
stream.closeEntry()
}
}
} }
return@newInstance getString(R.string.user_data_export_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
} }
@ -673,36 +664,50 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
checkStream.use { stream -> checkStream.use { stream ->
var ze: ZipEntry? = null var ze: ZipEntry? = null
while (stream.nextEntry?.also { ze = it } != null) { while (stream.nextEntry?.also { ze = it } != null) {
val itemName = ze!!.name.trim() if (ze!!.name.trim() == "/config/config.ini") {
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
isYuzuBackup = true isYuzuBackup = true
return@use return@use
} }
} }
} }
if (!isYuzuBackup) { if (!isYuzuBackup) {
return@newInstance MessageDialogFragment.newInstance( return@newInstance getString(R.string.invalid_yuzu_backup)
this,
titleId = R.string.invalid_yuzu_backup,
descriptionId = R.string.user_data_import_failed_description
)
} }
// Clear existing user data
File(DirectoryInitialization.userDirectory!!).deleteRecursively() File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage val zis =
try { ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
FileUtil.unzipToInternalStorage( val userDirectory = File(DirectoryInitialization.userDirectory!!)
BufferedInputStream(contentResolver.openInputStream(result)), val canonicalPath = userDirectory.canonicalPath + '/'
File(DirectoryInitialization.userDirectory!!) zis.use { stream ->
) var ze: ZipEntry? = stream.nextEntry
} catch (e: Exception) { while (ze != null) {
return@newInstance MessageDialogFragment.newInstance( val newFile = File(userDirectory, ze!!.name)
this, val destinationDirectory =
titleId = R.string.import_failed, if (ze!!.isDirectory) newFile else newFile.parentFile
descriptionId = R.string.user_data_import_failed_description
) if (!newFile.canonicalPath.startsWith(canonicalPath)) {
throw SecurityException(
"Zip file attempted path traversal! ${ze!!.name}"
)
}
if (!destinationDirectory.isDirectory && !destinationDirectory.mkdirs()) {
throw IOException("Failed to create directory $destinationDirectory")
}
if (!ze!!.isDirectory) {
val buffer = ByteArray(8096)
var read: Int
BufferedOutputStream(FileOutputStream(newFile)).use { bos ->
while (zis.read(buffer).also { read = it } != -1) {
bos.write(buffer, 0, read)
}
}
}
ze = stream.nextEntry
}
} }
// Reinitialize relevant data // Reinitialize relevant data
@ -712,146 +717,4 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return@newInstance getString(R.string.user_data_import_success) return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
} }
/**
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
* @return true if the zip file is successfully created, false otherwise.
*/
private fun zipSave(): Boolean {
try {
val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
tempFolder.mkdirs()
val saveFolder = File(savesFolderRoot)
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
val result = FileUtil.zipFromInternalStorage(
saveFolder,
savesFolderRoot,
BufferedOutputStream(FileOutputStream(outputZipFile))
)
if (result == TaskState.Failed) {
return false
}
lastZipCreated = outputZipFile
} catch (e: Exception) {
return false
}
return true
}
/**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/
fun exportSave() {
CoroutineScope(Dispatchers.IO).launch {
val wasZipCreated = zipSave()
val lastZipFile = lastZipCreated
if (!wasZipCreated || lastZipFile == null) {
withContext(Dispatchers.Main) {
Toast.makeText(
this@MainActivity,
getString(R.string.export_save_failed),
Toast.LENGTH_LONG
).show()
}
return@launch
}
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
this@MainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, "application/zip")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startForResultExportSave.launch(
Intent.createChooser(
intent,
getString(R.string.share_save_file)
)
)
}
}
}
private val startForResultExportSave =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
File(getPublicFilesDir().canonicalPath, "temp").deleteRecursively()
}
val importSaves =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
NativeLibrary.initializeEmptyUserDirectory()
val inputZip = contentResolver.openInputStream(result)
// A zip needs to have at least one subfolder named after a TitleId in order to be considered valid.
var validZip = false
val savesFolder = File(savesFolderRoot)
val cacheSaveDir = File("${applicationContext.cacheDir.path}/saves/")
cacheSaveDir.mkdir()
if (inputZip == null) {
Toast.makeText(
applicationContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
return@registerForActivityResult
}
val filterTitleId =
FilenameFilter { _, dirName -> dirName.matches(Regex("^0100[\\dA-Fa-f]{12}$")) }
try {
CoroutineScope(Dispatchers.IO).launch {
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
File(savesFolder, savePath).deleteRecursively()
File(cacheSaveDir, savePath).copyRecursively(
File(savesFolder, savePath),
true
)
validZip = true
}
withContext(Dispatchers.Main) {
if (!validZip) {
MessageDialogFragment.newInstance(
this@MainActivity,
titleId = R.string.save_file_invalid_zip_structure,
descriptionId = R.string.save_file_invalid_zip_structure_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@withContext
}
Toast.makeText(
applicationContext,
getString(R.string.save_file_imported_success),
Toast.LENGTH_LONG
).show()
}
cacheSaveDir.deleteRecursively()
}
} catch (e: Exception) {
Toast.makeText(
applicationContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
}
}
} }

View File

@ -8,7 +8,6 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.StateFlow
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -19,9 +18,6 @@ import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream
import java.util.zip.ZipOutputStream
object FileUtil { object FileUtil {
const val PATH_TREE = "tree" const val PATH_TREE = "tree"
@ -286,65 +282,30 @@ object FileUtil {
/** /**
* Extracts the given zip file into the given directory. * Extracts the given zip file into the given directory.
* @exception IOException if the file was being created outside of the target directory
*/ */
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun unzipToInternalStorage(zipStream: BufferedInputStream, destDir: File) { fun unzip(zipStream: InputStream, destDir: File): Boolean {
ZipInputStream(zipStream).use { zis -> ZipInputStream(BufferedInputStream(zipStream)).use { zis ->
var entry: ZipEntry? = zis.nextEntry var entry: ZipEntry? = zis.nextEntry
while (entry != null) { while (entry != null) {
val newFile = File(destDir, entry.name) val entryName = entry.name
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) {
if (!newFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) { throw SecurityException("Entry is outside of the target dir: " + entryFile.name)
throw SecurityException("Zip file attempted path traversal! ${entry.name}")
} }
if (entry.isDirectory) {
if (!destinationDirectory.isDirectory && !destinationDirectory.mkdirs()) { entryFile.mkdirs()
throw IOException("Failed to create directory $destinationDirectory") } else {
} entryFile.parentFile?.mkdirs()
entryFile.createNewFile()
if (!entry.isDirectory) { entryFile.outputStream().use { fos -> zis.copyTo(fos) }
newFile.outputStream().use { fos -> zis.copyTo(fos) }
} }
entry = zis.nextEntry entry = zis.nextEntry
} }
} }
}
/** return true
* Creates a zip file from a directory within internal storage
* @param inputFile File representation of the item that will be zipped
* @param rootDir Directory containing the inputFile
* @param outputStream Stream where the zip file will be output
*/
fun zipFromInternalStorage(
inputFile: File,
rootDir: String,
outputStream: BufferedOutputStream,
cancelled: StateFlow<Boolean>? = null
): TaskState {
try {
ZipOutputStream(outputStream).use { zos ->
inputFile.walkTopDown().forEach { file ->
if (cancelled?.value == true) {
return TaskState.Cancelled
}
if (!file.isDirectory) {
val entryName =
file.absolutePath.removePrefix(rootDir).removePrefix("/")
val entry = ZipEntry(entryName)
zos.putNextEntry(entry)
if (file.isFile) {
file.inputStream().use { fis -> fis.copyTo(zos) }
}
}
}
}
} catch (e: Exception) {
return TaskState.Failed
}
return TaskState.Completed
} }
fun isRootTreeUri(uri: Uri): Boolean { fun isRootTreeUri(uri: Uri): Boolean {

View File

@ -13,8 +13,6 @@
#include <android/api-level.h> #include <android/api-level.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include <common/fs/fs.h>
#include <core/file_sys/savedata_factory.h>
#include <core/loader/nro.h> #include <core/loader/nro.h>
#include <jni.h> #include <jni.h>
@ -104,7 +102,7 @@ public:
m_native_window = native_window; m_native_window = native_window;
} }
int InstallFileToNand(std::string filename, std::string file_extension) { int InstallFileToNand(std::string filename) {
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) { std::size_t block_size) {
if (src == nullptr || dest == nullptr) { if (src == nullptr || dest == nullptr) {
@ -136,11 +134,15 @@ public:
m_system.GetFileSystemController().CreateFactories(*m_vfs); m_system.GetFileSystemController().CreateFactories(*m_vfs);
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (file_extension == "nsp") { if (filename.ends_with("nsp")) {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) { if (nsp->IsExtractedType()) {
return InstallError; return InstallError;
} }
} else if (filename.ends_with("xci")) {
jconst xci =
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
nsp = xci->GetSecurePartitionNSP();
} else { } else {
return ErrorFilenameExtension; return ErrorFilenameExtension;
} }
@ -605,10 +607,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject
} }
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
jstring j_file, [[maybe_unused]] jstring j_file) {
jstring j_file_extension) { return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file),
GetJString(env, j_file_extension));
} }
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz, void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
@ -879,24 +879,4 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code); EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code);
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* env,
jobject instance) {
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0);
const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
if (!Common::FS::CreateParentDirs(full_path)) {
LOG_WARNING(Frontend, "Failed to create full path of the default user's save directory");
}
}
} // extern "C" } // extern "C"

View File

@ -22,7 +22,7 @@
<View <View
android:id="@+id/navigation_bar_shade" android:id="@+id/navigation_bar_shade"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="1px" android:layout_height="1px"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:clickable="false" android:clickable="false"

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="horizontal"
android:layout_gravity="center">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/user_data"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/user_data_description"
android:textAlignment="viewStart" />
</LinearLayout>
<Button
android:id="@+id/button_export"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/export"
android:tooltipText="@string/export"
android:visibility="gone"
app:icon="@drawable/ic_export"
tools:visibility="visible" />
<Button
android:id="@+id/button_install"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:contentDescription="@string/string_import"
android:tooltipText="@string/string_import"
android:visibility="gone"
app:icon="@drawable/ic_import"
tools:visibility="visible" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -176,6 +176,67 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:paddingHorizontal="16dp"
android:orientation="vertical"
android:layout_weight="1">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:textAlignment="viewStart"
android:text="@string/user_data" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
android:text="@string/user_data_description" />
</LinearLayout>
<Button
android:id="@+id/button_import"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/string_import"
android:tooltipText="@string/string_import"
app:icon="@drawable/ic_import" />
<Button
android:id="@+id/button_export"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/export"
android:tooltipText="@string/export"
app:icon="@drawable/ic_export" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -32,8 +32,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:focusable="false" android:focusable="false">
android:clickable="false">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loading_layout" android:id="@+id/loading_layout"
@ -156,7 +155,7 @@
android:id="@+id/in_game_menu" android:id="@+id/in_game_menu"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start|bottom"
app:headerLayout="@layout/header_in_game" app:headerLayout="@layout/header_in_game"
app:menu="@menu/menu_in_game" app:menu="@menu/menu_in_game"
tools:visibility="gone" /> tools:visibility="gone" />

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_licenses"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_installables"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_installables"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="@string/manage_yuzu_data"
app:navigationIcon="@drawable/ic_back" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_installables"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -19,9 +19,6 @@
<action <action
android:id="@+id/action_homeSettingsFragment_to_earlyAccessFragment" android:id="@+id/action_homeSettingsFragment_to_earlyAccessFragment"
app:destination="@id/earlyAccessFragment" /> app:destination="@id/earlyAccessFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
app:destination="@id/installableFragment" />
</fragment> </fragment>
<fragment <fragment
@ -91,9 +88,5 @@
<action <action
android:id="@+id/action_global_settingsActivity" android:id="@+id/action_global_settingsActivity"
app:destination="@id/settingsActivity" /> app:destination="@id/settingsActivity" />
<fragment
android:id="@+id/installableFragment"
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
android:label="InstallableFragment" />
</navigation> </navigation>

View File

@ -79,6 +79,7 @@
<string name="manage_save_data">Speicherdaten verwalten</string> <string name="manage_save_data">Speicherdaten verwalten</string>
<string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string> <string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string>
<string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string> <string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string>
<string name="import_export_saves_no_profile">Keine Speicherdaten gefunden. Bitte starte ein Spiel und versuche es erneut.</string>
<string name="save_file_imported_success">Erfolgreich importiert</string> <string name="save_file_imported_success">Erfolgreich importiert</string>
<string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string> <string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string>
<string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Administrar datos de guardado</string> <string name="manage_save_data">Administrar datos de guardado</string>
<string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string> <string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string>
<string name="import_export_saves_description">Importar o exportar archivos de guardado</string> <string name="import_export_saves_description">Importar o exportar archivos de guardado</string>
<string name="import_export_saves_no_profile">No se han encontrado datos de guardado. Por favor, ejecute un juego y vuelva a intentarlo.</string>
<string name="save_file_imported_success">Importado correctamente</string> <string name="save_file_imported_success">Importado correctamente</string>
<string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string> <string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string>
<string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Gérer les données de sauvegarde</string> <string name="manage_save_data">Gérer les données de sauvegarde</string>
<string name="manage_save_data_description">Données de sauvegarde trouvées. Veuillez sélectionner une option ci-dessous.</string> <string name="manage_save_data_description">Données de sauvegarde trouvées. Veuillez sélectionner une option ci-dessous.</string>
<string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string> <string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string>
<string name="import_export_saves_no_profile">Aucune données de sauvegarde trouvées. Veuillez lancer un jeu et réessayer.</string>
<string name="save_file_imported_success">Importé avec succès</string> <string name="save_file_imported_success">Importé avec succès</string>
<string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string> <string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string>
<string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Gestisci i salvataggi</string> <string name="manage_save_data">Gestisci i salvataggi</string>
<string name="manage_save_data_description">Salvataggio non trovato. Seleziona un\'opzione di seguito.</string> <string name="manage_save_data_description">Salvataggio non trovato. Seleziona un\'opzione di seguito.</string>
<string name="import_export_saves_description">Importa o esporta i salvataggi</string> <string name="import_export_saves_description">Importa o esporta i salvataggi</string>
<string name="import_export_saves_no_profile">Nessun salvataggio trovato. Avvia un gioco e riprova.</string>
<string name="save_file_imported_success">Importato con successo</string> <string name="save_file_imported_success">Importato con successo</string>
<string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string> <string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string>
<string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string>

View File

@ -80,6 +80,7 @@
<string name="manage_save_data">セーブデータを管理</string> <string name="manage_save_data">セーブデータを管理</string>
<string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string> <string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string>
<string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string> <string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string>
<string name="import_export_saves_no_profile">セーブデータがありません。ゲームを起動してから再度お試しください。</string>
<string name="save_file_imported_success">インポートが完了しました</string> <string name="save_file_imported_success">インポートが完了しました</string>
<string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string> <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string>
<string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string> <string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">저장 데이터 관리</string> <string name="manage_save_data">저장 데이터 관리</string>
<string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string> <string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string>
<string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string> <string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string>
<string name="import_export_saves_no_profile">저장 데이터를 찾을 수 없습니다. 게임을 실행한 후 다시 시도하세요.</string>
<string name="save_file_imported_success">가져오기 성공</string> <string name="save_file_imported_success">가져오기 성공</string>
<string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string> <string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string>
<string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string> <string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Administrere lagringsdata</string> <string name="manage_save_data">Administrere lagringsdata</string>
<string name="manage_save_data_description">Lagringsdata funnet. Velg et alternativ nedenfor.</string> <string name="manage_save_data_description">Lagringsdata funnet. Velg et alternativ nedenfor.</string>
<string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string> <string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string>
<string name="import_export_saves_no_profile">Ingen lagringsdata funnet. Start et nytt spill og prøv på nytt.</string>
<string name="save_file_imported_success">Vellykket import</string> <string name="save_file_imported_success">Vellykket import</string>
<string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string> <string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string>
<string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Zarządzaj plikami zapisów gier</string> <string name="manage_save_data">Zarządzaj plikami zapisów gier</string>
<string name="manage_save_data_description">Znaleziono pliki zapisów gier. Wybierz opcję poniżej.</string> <string name="manage_save_data_description">Znaleziono pliki zapisów gier. Wybierz opcję poniżej.</string>
<string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string> <string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string>
<string name="import_export_saves_no_profile">Nie znaleziono plików zapisów. Uruchom grę i spróbuj ponownie.</string>
<string name="save_file_imported_success">Zaimportowano pomyślnie</string> <string name="save_file_imported_success">Zaimportowano pomyślnie</string>
<string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string> <string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string>
<string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Gerir dados guardados</string> <string name="manage_save_data">Gerir dados guardados</string>
<string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string> <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
<string name="import_export_saves_description">Importa ou exporta dados guardados</string> <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
<string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
<string name="save_file_imported_success">Importado com sucesso</string> <string name="save_file_imported_success">Importado com sucesso</string>
<string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
<string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Gerir dados guardados</string> <string name="manage_save_data">Gerir dados guardados</string>
<string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string> <string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
<string name="import_export_saves_description">Importa ou exporta dados guardados</string> <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
<string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
<string name="save_file_imported_success">Importado com sucesso</string> <string name="save_file_imported_success">Importado com sucesso</string>
<string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
<string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Управление данными сохранений</string> <string name="manage_save_data">Управление данными сохранений</string>
<string name="manage_save_data_description">Найдено данные сохранений. Пожалуйста, выберите вариант ниже.</string> <string name="manage_save_data_description">Найдено данные сохранений. Пожалуйста, выберите вариант ниже.</string>
<string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string> <string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string>
<string name="import_export_saves_no_profile">Данные сохранений не найдены. Пожалуйста, запустите игру и повторите попытку.</string>
<string name="save_file_imported_success">Успешно импортировано</string> <string name="save_file_imported_success">Успешно импортировано</string>
<string name="save_file_invalid_zip_structure">Недопустимая структура папки сохранения</string> <string name="save_file_invalid_zip_structure">Недопустимая структура папки сохранения</string>
<string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string> <string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">Керування даними збережень</string> <string name="manage_save_data">Керування даними збережень</string>
<string name="manage_save_data_description">Знайдено дані збережень. Будь ласка, виберіть варіант нижче.</string> <string name="manage_save_data_description">Знайдено дані збережень. Будь ласка, виберіть варіант нижче.</string>
<string name="import_export_saves_description">Імпорт або експорт файлів збереження</string> <string name="import_export_saves_description">Імпорт або експорт файлів збереження</string>
<string name="import_export_saves_no_profile">Дані збережень не знайдено. Будь ласка, запустіть гру та повторіть спробу.</string>
<string name="save_file_imported_success">Успішно імпортовано</string> <string name="save_file_imported_success">Успішно імпортовано</string>
<string name="save_file_invalid_zip_structure">Неприпустима структура папки збереження</string> <string name="save_file_invalid_zip_structure">Неприпустима структура папки збереження</string>
<string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string> <string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="grid_columns">2</integer>
</resources>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">管理存档数据</string> <string name="manage_save_data">管理存档数据</string>
<string name="manage_save_data_description">已找到存档数据,请选择下方的选项。</string> <string name="manage_save_data_description">已找到存档数据,请选择下方的选项。</string>
<string name="import_export_saves_description">导入或导出存档</string> <string name="import_export_saves_description">导入或导出存档</string>
<string name="import_export_saves_no_profile">找不到存档数据,请启动游戏并重试。</string>
<string name="save_file_imported_success">已成功导入存档</string> <string name="save_file_imported_success">已成功导入存档</string>
<string name="save_file_invalid_zip_structure">无效的存档目录</string> <string name="save_file_invalid_zip_structure">无效的存档目录</string>
<string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string> <string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string>

View File

@ -81,6 +81,7 @@
<string name="manage_save_data">管理儲存資料</string> <string name="manage_save_data">管理儲存資料</string>
<string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string> <string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string>
<string name="import_export_saves_description">匯入或匯出儲存檔案</string> <string name="import_export_saves_description">匯入或匯出儲存檔案</string>
<string name="import_export_saves_no_profile">找不到儲存資料,請啟動遊戲並重試。</string>
<string name="save_file_imported_success">已成功匯入</string> <string name="save_file_imported_success">已成功匯入</string>
<string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string> <string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string>
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string> <string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<integer name="grid_columns">1</integer> <integer name="game_title_lines">2</integer>
<!-- Default SWITCH landscape layout --> <!-- Default SWITCH landscape layout -->
<integer name="SWITCH_BUTTON_A_X">760</integer> <integer name="SWITCH_BUTTON_A_X">760</integer>

View File

@ -90,6 +90,7 @@
<string name="manage_save_data">Manage save data</string> <string name="manage_save_data">Manage save data</string>
<string name="manage_save_data_description">Save data found. Please select an option below.</string> <string name="manage_save_data_description">Save data found. Please select an option below.</string>
<string name="import_export_saves_description">Import or export save files</string> <string name="import_export_saves_description">Import or export save files</string>
<string name="import_export_saves_no_profile">No save data found. Please launch a game and retry.</string>
<string name="save_file_imported_success">Imported successfully</string> <string name="save_file_imported_success">Imported successfully</string>
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string> <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@ -100,13 +101,12 @@
<string name="firmware_installing">Installing firmware</string> <string name="firmware_installing">Installing firmware</string>
<string name="firmware_installed_success">Firmware installed successfully</string> <string name="firmware_installed_success">Firmware installed successfully</string>
<string name="firmware_installed_failure">Firmware installation failed</string> <string name="firmware_installed_failure">Firmware installation failed</string>
<string name="firmware_installed_failure_description">Make sure the firmware nca files are at the root of the zip and try again.</string> <string name="firmware_installed_failure_description">Verify that the ZIP contains valid firmware and try again.</string>
<string name="share_log">Share debug logs</string> <string name="share_log">Share debug logs</string>
<string name="share_log_description">Share yuzu\'s log file to debug issues</string> <string name="share_log_description">Share yuzu\'s log file to debug issues</string>
<string name="share_log_missing">No log file found</string> <string name="share_log_missing">No log file found</string>
<string name="install_game_content">Install game content</string> <string name="install_game_content">Install game content</string>
<string name="install_game_content_description">Install game updates or DLC</string> <string name="install_game_content_description">Install game updates or DLC</string>
<string name="installing_game_content">Installing content…</string>
<string name="install_game_content_failure">Error installing file(s) to NAND</string> <string name="install_game_content_failure">Error installing file(s) to NAND</string>
<string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string> <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string> <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
@ -118,10 +118,6 @@
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
<string name="custom_driver_not_supported">Custom drivers not supported</string> <string name="custom_driver_not_supported">Custom drivers not supported</string>
<string name="custom_driver_not_supported_description">Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!</string> <string name="custom_driver_not_supported_description">Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!</string>
<string name="manage_yuzu_data">Manage yuzu data</string>
<string name="manage_yuzu_data_description">Import/export firmware, keys, user data, and more!</string>
<string name="share_save_file">Share save file</string>
<string name="export_save_failed">Failed to export save</string>
<!-- About screen strings --> <!-- About screen strings -->
<string name="gaia_is_not_real">Gaia isn\'t real</string> <string name="gaia_is_not_real">Gaia isn\'t real</string>
@ -141,7 +137,6 @@
<string name="user_data_export_success">User data exported successfully</string> <string name="user_data_export_success">User data exported successfully</string>
<string name="user_data_import_success">User data imported successfully</string> <string name="user_data_import_success">User data imported successfully</string>
<string name="user_data_export_cancelled">Export cancelled</string> <string name="user_data_export_cancelled">Export cancelled</string>
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
<string name="support_link">https://discord.gg/u77vRWY</string> <string name="support_link">https://discord.gg/u77vRWY</string>
<string name="website_link">https://yuzu-emu.org/</string> <string name="website_link">https://yuzu-emu.org/</string>
<string name="github_link">https://github.com/yuzu-emu</string> <string name="github_link">https://github.com/yuzu-emu</string>
@ -231,8 +226,6 @@
<string name="string_null">Null</string> <string name="string_null">Null</string>
<string name="string_import">Import</string> <string name="string_import">Import</string>
<string name="export">Export</string> <string name="export">Export</string>
<string name="export_failed">Export failed</string>
<string name="import_failed">Import failed</string>
<string name="cancelling">Cancelling</string> <string name="cancelling">Cancelling</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
@ -300,7 +293,6 @@
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
<string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
<string name="memory_formatted">%1$s %2$s</string> <string name="memory_formatted">%1$s %2$s</string>
<string name="no_game_present">No bootable game present!</string>
<!-- Region Names --> <!-- Region Names -->
<string name="region_japan">Japan</string> <string name="region_japan">Japan</string>

View File

@ -130,17 +130,13 @@ void LogSettings() {
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
} }
void UpdateGPUAccuracy() {
values.current_gpu_accuracy = values.gpu_accuracy.GetValue();
}
bool IsGPULevelExtreme() { bool IsGPULevelExtreme() {
return values.current_gpu_accuracy == GpuAccuracy::Extreme; return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme;
} }
bool IsGPULevelHigh() { bool IsGPULevelHigh() {
return values.current_gpu_accuracy == GpuAccuracy::Extreme || return values.gpu_accuracy.GetValue() == GpuAccuracy::Extreme ||
values.current_gpu_accuracy == GpuAccuracy::High; values.gpu_accuracy.GetValue() == GpuAccuracy::High;
} }
bool IsFastmemEnabled() { bool IsFastmemEnabled() {

View File

@ -307,7 +307,6 @@ struct Values {
Specialization::Default, Specialization::Default,
true, true,
true}; true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{ SwitchableSetting<AnisotropyMode, true> max_anisotropy{
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
"max_anisotropy", Category::RendererAdvanced}; "max_anisotropy", Category::RendererAdvanced};
@ -351,8 +350,6 @@ struct Values {
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug}; linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey", Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey",
Category::RendererDebug}; Category::RendererDebug};
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
bool renderer_amdvlk_depth_bias_workaround{};
// System // System
SwitchableSetting<Language, true> language_index{linkage, SwitchableSetting<Language, true> language_index{linkage,
@ -525,7 +522,6 @@ struct Values {
extern Values values; extern Values values;
void UpdateGPUAccuracy();
bool IsGPULevelExtreme(); bool IsGPULevelExtreme();
bool IsGPULevelHigh(); bool IsGPULevelHigh();

View File

@ -187,8 +187,6 @@ public:
this->SetValue(input == "true"); this->SetValue(input == "true");
} else if constexpr (std::is_same_v<Type, float>) { } else if constexpr (std::is_same_v<Type, float>) {
this->SetValue(std::stof(input)); this->SetValue(std::stof(input));
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
this->SetValue(ToEnum<AudioEngine>(input));
} else { } else {
this->SetValue(static_cast<Type>(std::stoll(input))); this->SetValue(static_cast<Type>(std::stoll(input)));
} }

View File

@ -381,10 +381,6 @@ struct System::Impl {
room_member->SendGameInfo(game_info); room_member->SendGameInfo(game_info);
} }
// Workarounds:
// Activate this in Super Smash Brothers Ultimate, it only affects AMD cards using AMDVLK
Settings::values.renderer_amdvlk_depth_bias_workaround = program_id == 0x1006A800016E000ULL;
status = SystemResultStatus::Success; status = SystemResultStatus::Success;
return status; return status;
} }
@ -444,9 +440,6 @@ struct System::Impl {
room_member->SendGameInfo(game_info); room_member->SendGameInfo(game_info);
} }
// Workarounds
Settings::values.renderer_amdvlk_depth_bias_workaround = false;
LOG_DEBUG(Core, "Shutdown OK"); LOG_DEBUG(Core, "Shutdown OK");
} }

View File

@ -32,7 +32,6 @@ struct CoreTiming::Event {
std::uintptr_t user_data; std::uintptr_t user_data;
std::weak_ptr<EventType> type; std::weak_ptr<EventType> type;
s64 reschedule_time; s64 reschedule_time;
heap_t::handle_type handle{};
// Sort by time, unless the times are the same, in which case sort by // Sort by time, unless the times are the same, in which case sort by
// the order added to the queue // the order added to the queue
@ -123,9 +122,9 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
std::scoped_lock scope{basic_lock}; std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
auto h{event_queue.emplace( event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})}; Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
(*h).handle = h; std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
} }
event.Set(); event.Set();
@ -139,9 +138,10 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::scoped_lock scope{basic_lock}; std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, event_queue.emplace_back(
resched_time.count()})}; Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
(*h).handle = h;
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
} }
event.Set(); event.Set();
@ -151,17 +151,15 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool wait) { std::uintptr_t user_data, bool wait) {
{ {
std::scoped_lock lk{basic_lock}; std::scoped_lock lk{basic_lock};
const auto itr =
std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get() && e.user_data == user_data;
});
std::vector<heap_t::handle_type> to_remove; // Removing random items breaks the invariant so we have to re-establish it.
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { if (itr != event_queue.end()) {
const Event& e = *itr; event_queue.erase(itr, event_queue.end());
if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
to_remove.push_back(itr->handle);
}
}
for (auto h : to_remove) {
event_queue.erase(h);
} }
} }
@ -202,45 +200,35 @@ std::optional<s64> CoreTiming::Advance() {
std::scoped_lock lock{advance_lock, basic_lock}; std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count(); global_timer = GetGlobalTimeNs().count();
while (!event_queue.empty() && event_queue.top().time <= global_timer) { while (!event_queue.empty() && event_queue.front().time <= global_timer) {
const Event& evt = event_queue.top(); Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
if (const auto event_type{evt.type.lock()}) { if (const auto event_type{evt.type.lock()}) {
if (evt.reschedule_time == 0) { basic_lock.unlock();
const auto evt_user_data = evt.user_data;
const auto evt_time = evt.time;
event_queue.pop(); const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
basic_lock.unlock(); basic_lock.lock();
event_type->callback(
evt_user_data, evt_time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
basic_lock.lock();
} else {
basic_lock.unlock();
const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
basic_lock.lock();
if (evt.reschedule_time != 0) {
const auto next_schedule_time{new_schedule_time.has_value() const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count() ? new_schedule_time.value().count()
: evt.reschedule_time}; : evt.reschedule_time};
// If this event was scheduled into a pause, its time now is going to be way // If this event was scheduled into a pause, its time now is going to be way behind.
// behind. Re-set this event to continue from the end of the pause. // Re-set this event to continue from the end of the pause.
auto next_time{evt.time + next_schedule_time}; auto next_time{evt.time + next_schedule_time};
if (evt.time < pause_end_time) { if (evt.time < pause_end_time) {
next_time = pause_end_time + next_schedule_time; next_time = pause_end_time + next_schedule_time;
} }
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, event_queue.emplace_back(
evt.type, next_schedule_time, evt.handle}); Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
} }
} }
@ -248,7 +236,7 @@ std::optional<s64> CoreTiming::Advance() {
} }
if (!event_queue.empty()) { if (!event_queue.empty()) {
return event_queue.top().time; return event_queue.front().time;
} else { } else {
return std::nullopt; return std::nullopt;
} }
@ -286,8 +274,7 @@ void CoreTiming::ThreadLoop() {
#endif #endif
} }
} else { } else {
// Queue is empty, wait until another event is scheduled and signals us to // Queue is empty, wait until another event is scheduled and signals us to continue.
// continue.
wait_set = true; wait_set = true;
event.Wait(); event.Wait();
} }

View File

@ -11,8 +11,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector>
#include <boost/heap/fibonacci_heap.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread.h" #include "common/thread.h"
@ -152,10 +151,11 @@ private:
s64 timer_resolution_ns; s64 timer_resolution_ns;
#endif #endif
using heap_t = // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>; // We don't use std::priority_queue because we need to be able to serialize, unserialize and
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
heap_t event_queue; // accommodated by the standard adaptor class.
std::vector<Event> event_queue;
u64 event_fifo_id = 0; u64 event_fifo_id = 0;
std::shared_ptr<EventType> ev_lost; std::shared_ptr<EventType> ev_lost;

View File

@ -542,7 +542,6 @@ void EmulatedController::UnloadInput() {
} }
void EmulatedController::EnableConfiguration() { void EmulatedController::EnableConfiguration() {
std::scoped_lock lock{connect_mutex, npad_mutex};
is_configuring = true; is_configuring = true;
tmp_is_connected = is_connected; tmp_is_connected = is_connected;
tmp_npad_type = npad_type; tmp_npad_type = npad_type;
@ -1557,7 +1556,7 @@ void EmulatedController::Connect(bool use_temporary_value) {
auto trigger_guard = auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
std::scoped_lock lock{connect_mutex, mutex}; std::scoped_lock lock{mutex};
if (is_configuring) { if (is_configuring) {
tmp_is_connected = true; tmp_is_connected = true;
return; return;
@ -1573,7 +1572,7 @@ void EmulatedController::Connect(bool use_temporary_value) {
void EmulatedController::Disconnect() { void EmulatedController::Disconnect() {
auto trigger_guard = auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
std::scoped_lock lock{connect_mutex, mutex}; std::scoped_lock lock{mutex};
if (is_configuring) { if (is_configuring) {
tmp_is_connected = false; tmp_is_connected = false;
return; return;
@ -1587,7 +1586,7 @@ void EmulatedController::Disconnect() {
} }
bool EmulatedController::IsConnected(bool get_temporary_value) const { bool EmulatedController::IsConnected(bool get_temporary_value) const {
std::scoped_lock lock{connect_mutex}; std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) { if (get_temporary_value && is_configuring) {
return tmp_is_connected; return tmp_is_connected;
} }
@ -1600,7 +1599,7 @@ NpadIdType EmulatedController::GetNpadIdType() const {
} }
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
std::scoped_lock lock{npad_mutex}; std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) { if (get_temporary_value && is_configuring) {
return tmp_npad_type; return tmp_npad_type;
} }
@ -1610,7 +1609,7 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
auto trigger_guard = auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
std::scoped_lock lock{mutex, npad_mutex}; std::scoped_lock lock{mutex};
if (is_configuring) { if (is_configuring) {
if (tmp_npad_type == npad_type_) { if (tmp_npad_type == npad_type_) {

View File

@ -603,8 +603,6 @@ private:
mutable std::mutex mutex; mutable std::mutex mutex;
mutable std::mutex callback_mutex; mutable std::mutex callback_mutex;
mutable std::mutex npad_mutex;
mutable std::mutex connect_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list; std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0; int last_callback_key = 0;

View File

@ -35,9 +35,7 @@ void KHardwareTimer::DoTask() {
} }
// Disable the timer interrupt while we handle this. // Disable the timer interrupt while we handle this.
// Not necessary due to core timing already having popped this event to call it. this->DisableInterrupt();
// this->DisableInterrupt();
m_wakeup_time = std::numeric_limits<s64>::max();
if (const s64 next_time = this->DoInterruptTaskImpl(GetTick()); if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
0 < next_time && next_time <= m_wakeup_time) { 0 < next_time && next_time <= m_wakeup_time) {

View File

@ -19,7 +19,6 @@
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applet_profile_select.h" #include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applet_web_browser.h"
#include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/applets/applets.h"
@ -191,7 +190,7 @@ IDisplayController::IDisplayController(Core::System& system_)
{5, nullptr, "GetLastForegroundCaptureImageEx"}, {5, nullptr, "GetLastForegroundCaptureImageEx"},
{6, nullptr, "GetLastApplicationCaptureImageEx"}, {6, nullptr, "GetLastApplicationCaptureImageEx"},
{7, nullptr, "GetCallerAppletCaptureImageEx"}, {7, nullptr, "GetCallerAppletCaptureImageEx"},
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, {8, nullptr, "TakeScreenShotOfOwnLayer"},
{9, nullptr, "CopyBetweenCaptureBuffers"}, {9, nullptr, "CopyBetweenCaptureBuffers"},
{10, nullptr, "AcquireLastApplicationCaptureBuffer"}, {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
@ -219,13 +218,6 @@ IDisplayController::IDisplayController(Core::System& system_)
IDisplayController::~IDisplayController() = default; IDisplayController::~IDisplayController() = default;
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
IDebugFunctions::IDebugFunctions(Core::System& system_) IDebugFunctions::IDebugFunctions(Core::System& system_)
: ServiceFramework{system_, "IDebugFunctions"} { : ServiceFramework{system_, "IDebugFunctions"} {
// clang-format off // clang-format off
@ -732,7 +724,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{110, nullptr, "OpenMyGpuErrorHandler"}, {110, nullptr, "OpenMyGpuErrorHandler"},
{120, nullptr, "GetAppletLaunchedHistory"}, {120, nullptr, "GetAppletLaunchedHistory"},
{200, nullptr, "GetOperationModeSystemInfo"}, {200, nullptr, "GetOperationModeSystemInfo"},
{300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"}, {300, nullptr, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"}, {400, nullptr, "ActivateMigrationService"},
{401, nullptr, "DeactivateMigrationService"}, {401, nullptr, "DeactivateMigrationService"},
{500, nullptr, "DisableSleepTillShutdown"}, {500, nullptr, "DisableSleepTillShutdown"},
@ -744,10 +736,6 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
// Configure applets to be in foreground state
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
} }
ICommonStateGetter::~ICommonStateGetter() = default; ICommonStateGetter::~ICommonStateGetter() = default;
@ -879,14 +867,6 @@ void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext&
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(SysPlatformRegion::Global);
}
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
HLERequestContext& ctx) { HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
@ -1344,19 +1324,18 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"} { : ServiceFramework{system_, "ILibraryAppletSelfAccessor"} {
// clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"}, {0, nullptr, "PopInData"},
{1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"}, {1, nullptr, "PushOutData"},
{2, nullptr, "PopInteractiveInData"}, {2, nullptr, "PopInteractiveInData"},
{3, nullptr, "PushInteractiveOutData"}, {3, nullptr, "PushInteractiveOutData"},
{5, nullptr, "GetPopInDataEvent"}, {5, nullptr, "GetPopInDataEvent"},
{6, nullptr, "GetPopInteractiveInDataEvent"}, {6, nullptr, "GetPopInteractiveInDataEvent"},
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, {10, nullptr, "ExitProcessAndReturn"},
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, {11, nullptr, "GetLibraryAppletInfo"},
{12, nullptr, "GetMainAppletIdentityInfo"}, {12, nullptr, "GetMainAppletIdentityInfo"},
{13, nullptr, "CanUseApplicationCore"}, {13, nullptr, "CanUseApplicationCore"},
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, {14, nullptr, "GetCallerAppletIdentityInfo"},
{15, nullptr, "GetMainAppletApplicationControlProperty"}, {15, nullptr, "GetMainAppletApplicationControlProperty"},
{16, nullptr, "GetMainAppletStorageId"}, {16, nullptr, "GetMainAppletStorageId"},
{17, nullptr, "GetCallerAppletIdentityInfoStack"}, {17, nullptr, "GetCallerAppletIdentityInfoStack"},
@ -1382,142 +1361,10 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
{140, nullptr, "SetApplicationMemoryReservation"}, {140, nullptr, "SetApplicationMemoryReservation"},
{150, nullptr, "ShouldSetGpuTimeSliceManually"}, {150, nullptr, "ShouldSetGpuTimeSliceManually"},
}; };
// clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
PushInShowMiiEditData();
} }
ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
if (queue_data.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultNoDataInChannel);
return;
}
auto data = queue_data.front();
queue_data.pop_front();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(system, std::move(data));
}
void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
system.Exit();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
struct LibraryAppletInfo {
Applets::AppletId applet_id;
Applets::LibraryAppletMode library_applet_mode;
};
LOG_WARNING(Service_AM, "(STUBBED) called");
const LibraryAppletInfo applet_info{
.applet_id = Applets::AppletId::MiiEdit,
.library_applet_mode = Applets::LibraryAppletMode::AllForeground,
};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
struct AppletIdentityInfo {
Applets::AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
.applet_id = Applets::AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::PushInShowMiiEditData() {
struct MiiEditV3 {
Applets::MiiEditAppletInputCommon common;
Applets::MiiEditAppletInputV3 input;
};
static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
MiiEditV3 mii_arguments{
.common =
{
.version = Applets::MiiEditAppletVersion::Version3,
.applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit,
},
.input{},
};
std::vector<u8> argument_data(sizeof(mii_arguments));
std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
queue_data.emplace_back(std::move(argument_data));
}
IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_)
: ServiceFramework{system_, "IAppletCommonFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetTerminateResult"},
{10, nullptr, "ReadThemeStorage"},
{11, nullptr, "WriteThemeStorage"},
{20, nullptr, "PushToAppletBoundChannel"},
{21, nullptr, "TryPopFromAppletBoundChannel"},
{40, nullptr, "GetDisplayLogicalResolution"},
{42, nullptr, "SetDisplayMagnification"},
{50, nullptr, "SetHomeButtonDoubleClickEnabled"},
{51, nullptr, "GetHomeButtonDoubleClickEnabled"},
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
{60, nullptr, "IsVrModeCurtainRequired"},
{61, nullptr, "IsSleepRequiredByHighTemperature"},
{62, nullptr, "IsSleepRequiredByLowBattery"},
{70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
{80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
{81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
{90, nullptr, "OpenNamedChannelAsParent"},
{91, nullptr, "OpenNamedChannelAsChild"},
{100, nullptr, "SetApplicationCoreUsageMode"},
};
// clang-format on
RegisterHandlers(functions);
}
IAppletCommonFunctions::~IAppletCommonFunctions() = default;
void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
IApplicationFunctions::IApplicationFunctions(Core::System& system_) IApplicationFunctions::IApplicationFunctions(Core::System& system_)
: ServiceFramework{system_, "IApplicationFunctions"}, service_context{system, : ServiceFramework{system_, "IApplicationFunctions"}, service_context{system,
@ -2094,6 +1941,9 @@ void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>(system); auto message_queue = std::make_shared<AppletMessageQueue>(system);
// Needed on game boot
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService( server_manager->RegisterNamedService(
@ -2199,8 +2049,8 @@ IProcessWindingController::IProcessWindingController(Core::System& system_)
: ServiceFramework{system_, "IProcessWindingController"} { : ServiceFramework{system_, "IProcessWindingController"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"}, {0, nullptr, "GetLaunchReason"},
{11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"}, {11, nullptr, "OpenCallingLibraryApplet"},
{21, nullptr, "PushContext"}, {21, nullptr, "PushContext"},
{22, nullptr, "PopContext"}, {22, nullptr, "PopContext"},
{23, nullptr, "CancelWindingReservation"}, {23, nullptr, "CancelWindingReservation"},
@ -2214,46 +2064,4 @@ IProcessWindingController::IProcessWindingController(Core::System& system_)
} }
IProcessWindingController::~IProcessWindingController() = default; IProcessWindingController::~IProcessWindingController() = default;
void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
struct AppletProcessLaunchReason {
u8 flag;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
"AppletProcessLaunchReason is an invalid size");
AppletProcessLaunchReason reason{
.flag = 0,
};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(reason);
}
void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
const auto applet_id = Applets::AppletId::MiiEdit;
const auto applet_mode = Applets::LibraryAppletMode::AllForeground;
LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id,
applet_mode);
const auto& applet_manager{system.GetAppletManager()};
const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
if (applet == nullptr) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
}
} // namespace Service::AM } // namespace Service::AM

View File

@ -120,9 +120,6 @@ class IDisplayController final : public ServiceFramework<IDisplayController> {
public: public:
explicit IDisplayController(Core::System& system_); explicit IDisplayController(Core::System& system_);
~IDisplayController() override; ~IDisplayController() override;
private:
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
}; };
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
@ -215,11 +212,6 @@ private:
CaptureButtonLongPressing, CaptureButtonLongPressing,
}; };
enum class SysPlatformRegion : s32 {
Global = 1,
Terra = 2,
};
void GetEventHandle(HLERequestContext& ctx); void GetEventHandle(HLERequestContext& ctx);
void ReceiveMessage(HLERequestContext& ctx); void ReceiveMessage(HLERequestContext& ctx);
void GetCurrentFocusState(HLERequestContext& ctx); void GetCurrentFocusState(HLERequestContext& ctx);
@ -235,7 +227,6 @@ private:
void GetDefaultDisplayResolution(HLERequestContext& ctx); void GetDefaultDisplayResolution(HLERequestContext& ctx);
void SetCpuBoostMode(HLERequestContext& ctx); void SetCpuBoostMode(HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
void GetSettingsPlatformRegion(HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue; std::shared_ptr<AppletMessageQueue> msg_queue;
@ -303,26 +294,6 @@ class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletS
public: public:
explicit ILibraryAppletSelfAccessor(Core::System& system_); explicit ILibraryAppletSelfAccessor(Core::System& system_);
~ILibraryAppletSelfAccessor() override; ~ILibraryAppletSelfAccessor() override;
private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void PushInShowMiiEditData();
std::deque<std::vector<u8>> queue_data;
};
class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
public:
explicit IAppletCommonFunctions(Core::System& system_);
~IAppletCommonFunctions() override;
private:
void SetCpuBoostRequestPriority(HLERequestContext& ctx);
}; };
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@ -407,10 +378,6 @@ class IProcessWindingController final : public ServiceFramework<IProcessWindingC
public: public:
explicit IProcessWindingController(Core::System& system_); explicit IProcessWindingController(Core::System& system_);
~IProcessWindingController() override; ~IProcessWindingController() override;
private:
void GetLaunchReason(HLERequestContext& ctx);
void OpenCallingLibraryApplet(HLERequestContext& ctx);
}; };
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);

View File

@ -27,7 +27,7 @@ public:
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
{21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, {21, nullptr, "GetAppletCommonFunctions"},
{22, nullptr, "GetHomeMenuFunctions"}, {22, nullptr, "GetHomeMenuFunctions"},
{23, nullptr, "GetGlobalStateController"}, {23, nullptr, "GetGlobalStateController"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
@ -86,6 +86,14 @@ private:
rb.PushIpcInterface<IProcessWindingController>(system); rb.PushIpcInterface<IProcessWindingController>(system);
} }
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) { void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
@ -102,22 +110,6 @@ private:
rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system); rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system);
} }
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue; std::shared_ptr<AppletMessageQueue> msg_queue;
}; };
@ -141,7 +133,7 @@ public:
{20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
{23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, {23, nullptr, "GetAppletCommonFunctions"},
{1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
}; };
// clang-format on // clang-format on
@ -190,6 +182,14 @@ private:
rb.PushIpcInterface<IDisplayController>(system); rb.PushIpcInterface<IDisplayController>(system);
} }
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) { void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
@ -222,22 +222,6 @@ private:
rb.PushIpcInterface<IApplicationCreator>(system); rb.PushIpcInterface<IApplicationCreator>(system);
} }
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue; std::shared_ptr<AppletMessageQueue> msg_queue;
}; };

View File

@ -7,9 +7,7 @@
#include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/mii_edit.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_mii_edit.h" #include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Applets { namespace Service::AM::Applets {
@ -58,12 +56,6 @@ void MiiEdit::Initialize() {
sizeof(MiiEditAppletInputV4)); sizeof(MiiEditAppletInputV4));
break; break;
} }
manager = system.ServiceManager().GetService<Mii::MiiDBModule>("mii:e")->GetMiiManager();
if (manager == nullptr) {
manager = std::make_shared<Mii::MiiManager>();
}
manager->Initialize(metadata);
} }
bool MiiEdit::TransactionComplete() const { bool MiiEdit::TransactionComplete() const {
@ -86,46 +78,22 @@ void MiiEdit::Execute() {
// This is a default stub for each of the MiiEdit applet modes. // This is a default stub for each of the MiiEdit applet modes.
switch (applet_input_common.applet_mode) { switch (applet_input_common.applet_mode) {
case MiiEditAppletMode::ShowMiiEdit: case MiiEditAppletMode::ShowMiiEdit:
case MiiEditAppletMode::AppendMii:
case MiiEditAppletMode::AppendMiiImage: case MiiEditAppletMode::AppendMiiImage:
case MiiEditAppletMode::UpdateMiiImage: case MiiEditAppletMode::UpdateMiiImage:
MiiEditOutput(MiiEditResult::Success, 0); MiiEditOutput(MiiEditResult::Success, 0);
break; break;
case MiiEditAppletMode::AppendMii: { case MiiEditAppletMode::CreateMii:
Mii::StoreData store_data{};
store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
store_data.SetNickname({u'y', u'u', u'z', u'u'});
store_data.SetChecksum();
const auto result = manager->AddOrReplace(metadata, store_data);
if (result.IsError()) {
MiiEditOutput(MiiEditResult::Cancel, 0);
break;
}
s32 index = manager->FindIndex(store_data.GetCreateId(), false);
if (index == -1) {
MiiEditOutput(MiiEditResult::Cancel, 0);
break;
}
MiiEditOutput(MiiEditResult::Success, index);
break;
}
case MiiEditAppletMode::CreateMii: {
Mii::CharInfo char_info{};
manager->BuildRandom(char_info, Mii::Age::All, Mii::Gender::All, Mii::Race::All);
const MiiEditCharInfo edit_char_info{
.mii_info{char_info},
};
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
break;
}
case MiiEditAppletMode::EditMii: { case MiiEditAppletMode::EditMii: {
Mii::CharInfo char_info{};
Mii::StoreData store_data{};
store_data.BuildBase(Mii::Gender::Male);
char_info.SetFromStoreData(store_data);
const MiiEditCharInfo edit_char_info{ const MiiEditCharInfo edit_char_info{
.mii_info{applet_input_v4.char_info.mii_info}, .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
? applet_input_v4.char_info.mii_info
: char_info},
}; };
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info); MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
@ -145,8 +113,6 @@ void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
.index{index}, .index{index},
}; };
LOG_INFO(Input, "called, result={}, index={}", result, index);
std::vector<u8> out_data(sizeof(MiiEditAppletOutput)); std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput)); std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));

View File

@ -11,11 +11,6 @@ namespace Core {
class System; class System;
} // namespace Core } // namespace Core
namespace Service::Mii {
struct DatabaseSessionMetadata;
class MiiManager;
} // namespace Service::Mii
namespace Service::AM::Applets { namespace Service::AM::Applets {
class MiiEdit final : public Applet { class MiiEdit final : public Applet {
@ -45,8 +40,6 @@ private:
MiiEditAppletInputV4 applet_input_v4{}; MiiEditAppletInputV4 applet_input_v4{};
bool is_complete{false}; bool is_complete{false};
std::shared_ptr<Mii::MiiManager> manager = nullptr;
Mii::DatabaseSessionMetadata metadata{};
}; };
} // namespace Service::AM::Applets } // namespace Service::AM::Applets

View File

@ -329,7 +329,6 @@ public:
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"}, {15, nullptr, "QueryEntry"},
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
} }
@ -522,46 +521,6 @@ public:
rb.PushRaw(vfs_timestamp); rb.PushRaw(vfs_timestamp);
} }
void GetFileSystemAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xc0,
"FileSystemAttribute has incorrect size");
FileSystemAttribute savedata_attribute{};
savedata_attribute.dir_entry_name_length_max_defined = true;
savedata_attribute.file_entry_name_length_max_defined = true;
savedata_attribute.dir_entry_name_length_max = 0x40;
savedata_attribute.file_entry_name_length_max = 0x40;
IPC::ResponseBuilder rb{ctx, 50};
rb.Push(ResultSuccess);
rb.PushRaw(savedata_attribute);
}
private: private:
VfsDirectoryServiceWrapper backend; VfsDirectoryServiceWrapper backend;
SizeGetter size; SizeGetter size;
@ -739,7 +698,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{19, nullptr, "FormatSdCardFileSystem"}, {19, nullptr, "FormatSdCardFileSystem"},
{21, nullptr, "DeleteSaveDataFileSystem"}, {21, nullptr, "DeleteSaveDataFileSystem"},
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"}, {22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
{23, &FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId, "CreateSaveDataFileSystemBySystemSaveDataId"}, {23, nullptr, "CreateSaveDataFileSystemBySystemSaveDataId"},
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"}, {24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"}, {25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
{26, nullptr, "FormatSdCardDryRun"}, {26, nullptr, "FormatSdCardDryRun"},
@ -753,7 +712,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"}, {35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
{36, nullptr, "OpenHostFileSystemWithOption"}, {36, nullptr, "OpenHostFileSystemWithOption"},
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"}, {51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
{52, &FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId, "OpenSaveDataFileSystemBySystemSaveDataId"}, {52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"}, {53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"}, {57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"}, {58, nullptr, "ReadSaveDataFileSystemExtraData"},
@ -911,21 +870,6 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
FileSys::VirtualDir save_data_dir{};
fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
@ -972,11 +916,6 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
} }
void FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx);
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) { void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem"); LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx); OpenSaveDataFileSystem(ctx);

View File

@ -39,9 +39,7 @@ private:
void OpenFileSystemWithPatch(HLERequestContext& ctx); void OpenFileSystemWithPatch(HLERequestContext& ctx);
void OpenSdCardFileSystem(HLERequestContext& ctx); void OpenSdCardFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystem(HLERequestContext& ctx); void CreateSaveDataFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenSaveDataFileSystem(HLERequestContext& ctx); void OpenSaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx); void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx);
void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx); void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx);

View File

@ -346,7 +346,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
} }
SignalStyleSetChangedEvent(npad_id); SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(controller.shared_memory); WriteEmptyEntry(controller.shared_memory);
hid_core.SetLastActiveController(npad_id);
} }
void Controller_NPad::OnInit() { void Controller_NPad::OnInit() {

View File

@ -18,10 +18,8 @@ namespace Service::Mii {
class IDatabaseService final : public ServiceFramework<IDatabaseService> { class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public: public:
explicit IDatabaseService(Core::System& system_, std::shared_ptr<MiiManager> mii_manager, explicit IDatabaseService(Core::System& system_, bool is_system_)
bool is_system_) : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
: ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, is_system{
is_system_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IDatabaseService::IsUpdated, "IsUpdated"}, {0, &IDatabaseService::IsUpdated, "IsUpdated"},
@ -56,7 +54,7 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
manager->Initialize(metadata); manager.Initialize(metadata);
} }
private: private:
@ -66,7 +64,7 @@ private:
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const bool is_updated = manager->IsUpdated(metadata, source_flag); const bool is_updated = manager.IsUpdated(metadata, source_flag);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -76,7 +74,7 @@ private:
void IsFullDatabase(HLERequestContext& ctx) { void IsFullDatabase(HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called"); LOG_DEBUG(Service_Mii, "called");
const bool is_full_database = manager->IsFullDatabase(); const bool is_full_database = manager.IsFullDatabase();
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -87,7 +85,7 @@ private:
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto source_flag{rp.PopRaw<SourceFlag>()}; const auto source_flag{rp.PopRaw<SourceFlag>()};
const u32 mii_count = manager->GetCount(metadata, source_flag); const u32 mii_count = manager.GetCount(metadata, source_flag);
LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count); LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
@ -103,7 +101,7 @@ private:
u32 mii_count{}; u32 mii_count{};
std::vector<CharInfoElement> char_info_elements(output_size); std::vector<CharInfoElement> char_info_elements(output_size);
const auto result = manager->Get(metadata, char_info_elements, mii_count, source_flag); const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(char_info_elements); ctx.WriteBuffer(char_info_elements);
@ -124,7 +122,7 @@ private:
u32 mii_count{}; u32 mii_count{};
std::vector<CharInfo> char_info(output_size); std::vector<CharInfo> char_info(output_size);
const auto result = manager->Get(metadata, char_info, mii_count, source_flag); const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(char_info); ctx.WriteBuffer(char_info);
@ -146,7 +144,7 @@ private:
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag); LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
CharInfo new_char_info{}; CharInfo new_char_info{};
const auto result = manager->UpdateLatest(metadata, new_char_info, char_info, source_flag); const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
if (result.IsFailure()) { if (result.IsFailure()) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); rb.Push(result);
@ -185,7 +183,7 @@ private:
} }
CharInfo char_info{}; CharInfo char_info{};
manager->BuildRandom(char_info, age, gender, race); manager.BuildRandom(char_info, age, gender, race);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -205,7 +203,7 @@ private:
} }
CharInfo char_info{}; CharInfo char_info{};
manager->BuildDefault(char_info, index); manager.BuildDefault(char_info, index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -219,7 +217,7 @@ private:
u32 mii_count{}; u32 mii_count{};
std::vector<StoreDataElement> store_data_elements(output_size); std::vector<StoreDataElement> store_data_elements(output_size);
const auto result = manager->Get(metadata, store_data_elements, mii_count, source_flag); const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(store_data_elements); ctx.WriteBuffer(store_data_elements);
@ -240,7 +238,7 @@ private:
u32 mii_count{}; u32 mii_count{};
std::vector<StoreData> store_data(output_size); std::vector<StoreData> store_data(output_size);
const auto result = manager->Get(metadata, store_data, mii_count, source_flag); const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
if (mii_count != 0) { if (mii_count != 0) {
ctx.WriteBuffer(store_data); ctx.WriteBuffer(store_data);
@ -268,7 +266,7 @@ private:
StoreData new_store_data{}; StoreData new_store_data{};
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->UpdateLatest(metadata, new_store_data, store_data, source_flag); result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
} }
if (result.IsFailure()) { if (result.IsFailure()) {
@ -290,7 +288,7 @@ private:
LOG_INFO(Service_Mii, "called with create_id={}, is_special={}", LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
create_id.FormattedString(), is_special); create_id.FormattedString(), is_special);
const s32 index = manager->FindIndex(create_id, is_special); const s32 index = manager.FindIndex(create_id, is_special);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -311,14 +309,14 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
const u32 count = manager->GetCount(metadata, SourceFlag::Database); const u32 count = manager.GetCount(metadata, SourceFlag::Database);
if (new_index < 0 || new_index >= static_cast<s32>(count)) { if (new_index < 0 || new_index >= static_cast<s32>(count)) {
result = ResultInvalidArgument; result = ResultInvalidArgument;
} }
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->Move(metadata, new_index, create_id); result = manager.Move(metadata, new_index, create_id);
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -338,7 +336,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->AddOrReplace(metadata, store_data); result = manager.AddOrReplace(metadata, store_data);
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -358,7 +356,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->Delete(metadata, create_id); result = manager.Delete(metadata, create_id);
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -378,7 +376,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->DestroyFile(metadata); result = manager.DestroyFile(metadata);
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -398,7 +396,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->DeleteFile(); result = manager.DeleteFile();
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -418,7 +416,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
result = manager->Format(metadata); result = manager.Format(metadata);
} }
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -436,7 +434,7 @@ private:
} }
if (result.IsSuccess()) { if (result.IsSuccess()) {
is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata); is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
} }
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
@ -451,7 +449,7 @@ private:
LOG_DEBUG(Service_Mii, "called"); LOG_DEBUG(Service_Mii, "called");
s32 index{}; s32 index{};
const auto result = manager->GetIndex(metadata, info, index); const auto result = manager.GetIndex(metadata, info, index);
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result); rb.Push(result);
@ -464,7 +462,7 @@ private:
LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version); LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
manager->SetInterfaceVersion(metadata, interface_version); manager.SetInterfaceVersion(metadata, interface_version);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -477,7 +475,7 @@ private:
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
CharInfo char_info{}; CharInfo char_info{};
const auto result = manager->ConvertV3ToCharInfo(char_info, mii_v3); const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(result); rb.Push(result);
@ -491,7 +489,7 @@ private:
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
CharInfo char_info{}; CharInfo char_info{};
const auto result = manager->ConvertCoreDataToCharInfo(char_info, core_data); const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(result); rb.Push(result);
@ -505,7 +503,7 @@ private:
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
CoreData core_data{}; CoreData core_data{};
const auto result = manager->ConvertCharInfoToCoreData(core_data, char_info); const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
rb.Push(result); rb.Push(result);
@ -518,46 +516,41 @@ private:
LOG_INFO(Service_Mii, "called"); LOG_INFO(Service_Mii, "called");
const auto result = manager->Append(metadata, char_info); const auto result = manager.Append(metadata, char_info);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); rb.Push(result);
} }
std::shared_ptr<MiiManager> manager = nullptr; MiiManager manager{};
DatabaseSessionMetadata metadata{}; DatabaseSessionMetadata metadata{};
bool is_system{}; bool is_system{};
}; };
MiiDBModule::MiiDBModule(Core::System& system_, const char* name_, class MiiDBModule final : public ServiceFramework<MiiDBModule> {
std::shared_ptr<MiiManager> mii_manager, bool is_system_) public:
: ServiceFramework{system_, name_}, manager{mii_manager}, is_system{is_system_} { explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
// clang-format off : ServiceFramework{system_, name_}, is_system{is_system_} {
static const FunctionInfo functions[] = { // clang-format off
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, static const FunctionInfo functions[] = {
}; {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
// clang-format on };
// clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
if (manager == nullptr) {
manager = std::make_shared<MiiManager>();
} }
}
MiiDBModule::~MiiDBModule() = default; private:
void GetDatabaseService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system, is_system);
void MiiDBModule::GetDatabaseService(HLERequestContext& ctx) { LOG_DEBUG(Service_Mii, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; }
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDatabaseService>(system, manager, is_system);
LOG_DEBUG(Service_Mii, "called"); bool is_system{};
} };
std::shared_ptr<MiiManager> MiiDBModule::GetMiiManager() {
return manager;
}
class MiiImg final : public ServiceFramework<MiiImg> { class MiiImg final : public ServiceFramework<MiiImg> {
public: public:
@ -603,12 +596,11 @@ private:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
std::shared_ptr<MiiManager> manager = nullptr;
server_manager->RegisterNamedService( server_manager->RegisterNamedService("mii:e",
"mii:e", std::make_shared<MiiDBModule>(system, "mii:e", manager, true)); std::make_shared<MiiDBModule>(system, "mii:e", true));
server_manager->RegisterNamedService( server_manager->RegisterNamedService("mii:u",
"mii:u", std::make_shared<MiiDBModule>(system, "mii:u", manager, false)); std::make_shared<MiiDBModule>(system, "mii:u", false));
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View File

@ -3,29 +3,11 @@
#pragma once #pragma once
#include "core/hle/service/service.h"
namespace Core { namespace Core {
class System; class System;
} }
namespace Service::Mii { namespace Service::Mii {
class MiiManager;
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
public:
explicit MiiDBModule(Core::System& system_, const char* name_,
std::shared_ptr<MiiManager> mii_manager, bool is_system_);
~MiiDBModule() override;
std::shared_ptr<MiiManager> GetMiiManager();
private:
void GetDatabaseService(HLERequestContext& ctx);
std::shared_ptr<MiiManager> manager = nullptr;
bool is_system{};
};
void LoopProcess(Core::System& system); void LoopProcess(Core::System& system);

View File

@ -168,7 +168,7 @@ Result DatabaseManager::FindIndex(s32& out_index, const Common::UUID& create_id,
return ResultSuccess; return ResultSuccess;
} }
for (std::size_t i = 0; i < index; ++i) { for (std::size_t i = 0; i <= index; ++i) {
if (database.Get(i).IsSpecial()) { if (database.Get(i).IsSpecial()) {
continue; continue;
} }

View File

@ -130,11 +130,11 @@ Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharI
} }
s32 index{}; s32 index{};
const bool is_special = metadata.magic == MiiMagic; Result result = {};
const auto result = database_manager.FindIndex(index, char_info.GetCreateId(), is_special); // FindIndex(index);
if (result.IsError()) { if (result.IsError()) {
index = -1; return ResultNotFound;
} }
if (index == -1) { if (index == -1) {

View File

@ -614,7 +614,7 @@ struct Nickname {
} }
std::size_t index = 1; std::size_t index = 1;
while (index < MaxNameSize && data[index] != 0) { while (data[index] != 0) {
index++; index++;
} }
while (index < MaxNameSize && data[index] == 0) { while (index < MaxNameSize && data[index] == 0) {

View File

@ -113,7 +113,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
.values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); .values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
const auto eyebrow_y{race == Race::Asian ? 6 : 7}; const auto eyebrow_y{race == Race::Asian ? 9 : 10};
const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6}; const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6};
const auto eyebrow_rotate{ const auto eyebrow_rotate{
32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]}; 32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]};
@ -171,7 +171,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
u8 glasses_type{}; u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) { while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) { if (++glasses_type >= glasses_type_info.values_count) {
glasses_type = 0; ASSERT(false);
break; break;
} }
} }
@ -179,7 +179,6 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
SetGlassType(static_cast<GlassType>(glasses_type)); SetGlassType(static_cast<GlassType>(glasses_type));
SetGlassColor(RawData::GetGlassColorFromVer3(0)); SetGlassColor(RawData::GetGlassColorFromVer3(0));
SetGlassScale(4); SetGlassScale(4);
SetGlassY(static_cast<u8>(axis_y + 10));
SetMoleType(MoleType::None); SetMoleType(MoleType::None);
SetMoleScale(4); SetMoleScale(4);

View File

@ -1716,18 +1716,18 @@ const std::array<RandomMiiData4, 18> RandomMiiMouthType{
const std::array<RandomMiiData2, 3> RandomMiiGlassType{ const std::array<RandomMiiData2, 3> RandomMiiGlassType{
RandomMiiData2{ RandomMiiData2{
.arg_1 = 0, .arg_1 = 0,
.values_count = 4, .values_count = 9,
.values = {90, 94, 96, 100}, .values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
}, },
RandomMiiData2{ RandomMiiData2{
.arg_1 = 1, .arg_1 = 1,
.values_count = 8, .values_count = 9,
.values = {83, 86, 90, 93, 94, 96, 98, 100}, .values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
}, },
RandomMiiData2{ RandomMiiData2{
.arg_1 = 2, .arg_1 = 2,
.values_count = 8, .values_count = 9,
.values = {78, 83, 0, 93, 0, 0, 98, 100}, .values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
}, },
}; };

View File

@ -1374,7 +1374,7 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co
// Convert from utf16 to utf8 // Convert from utf16 to utf8
const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size() - 1);
return amiibo_name; return amiibo_name;
} }

View File

@ -144,7 +144,7 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
{3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
{4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
{5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriorityForSystem"}, {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"}, {100, nullptr, "RequestApplicationFunctionAuthorization"},
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
@ -262,17 +262,8 @@ void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx
} }
void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) { void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) {
// The maximum number of elements that can be returned is 6. Regardless of the available fonts
// or buffer size.
constexpr std::size_t MaxElementCount = 6;
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
const std::size_t font_codes_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(0));
const std::size_t font_offsets_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(1));
const std::size_t font_sizes_count =
std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(2));
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
@ -289,9 +280,9 @@ void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext&
} }
// Resize buffers if game requests smaller size output // Resize buffers if game requests smaller size output
font_codes.resize(std::min(font_codes.size(), font_codes_count)); font_codes.resize(std::min(font_codes.size(), ctx.GetWriteBufferNumElements<u32>(0)));
font_offsets.resize(std::min(font_offsets.size(), font_offsets_count)); font_offsets.resize(std::min(font_offsets.size(), ctx.GetWriteBufferNumElements<u32>(1)));
font_sizes.resize(std::min(font_sizes.size(), font_sizes_count)); font_sizes.resize(std::min(font_sizes.size(), ctx.GetWriteBufferNumElements<u32>(2)));
ctx.WriteBuffer(font_codes, 0); ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1); ctx.WriteBuffer(font_offsets, 1);

View File

@ -7,12 +7,15 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
Id Image(EmitContext& ctx, IR::TextureInstInfo info) { Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
if (!index.IsImmediate()) {
throw NotImplementedException("Indirect image indexing");
}
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
return def.id; return def.id;
} else { } else {
const ImageDefinition def{ctx.images.at(info.descriptor_index)}; const ImageDefinition def{ctx.images.at(index.U32())};
return def.id; return def.id;
} }
} }
@ -25,12 +28,8 @@ std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value, Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
if (!index.IsImmediate() || index.U32() != 0) {
// TODO: handle layers
throw NotImplementedException("Image indexing");
}
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id image{Image(ctx, info)}; const Id image{Image(ctx, index, info)};
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))}; const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
const auto [scope, semantics]{AtomicArgs(ctx)}; const auto [scope, semantics]{AtomicArgs(ctx)};
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);

View File

@ -74,6 +74,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
throw InvalidArgument("Invalid image format {}", format); throw InvalidArgument("Invalid image format {}", format);
} }
spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) {
const auto spv_format = GetImageFormat(format);
return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format;
}
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
const spv::ImageFormat format{GetImageFormat(desc.format)}; const spv::ImageFormat format{GetImageFormat(desc.format)};
const Id type{ctx.U32[1]}; const Id type{ctx.U32[1]};
@ -1270,7 +1275,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
if (desc.count != 1) { if (desc.count != 1) {
throw NotImplementedException("Array of image buffers"); throw NotImplementedException("Array of image buffers");
} }
const spv::ImageFormat format{GetImageFormat(desc.format)}; const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)};
const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};

View File

@ -95,12 +95,6 @@ add_library(video_core STATIC
memory_manager.h memory_manager.h
precompiled_headers.h precompiled_headers.h
pte_kind.h pte_kind.h
query_cache/bank_base.h
query_cache/query_base.h
query_cache/query_cache_base.h
query_cache/query_cache.h
query_cache/query_stream.h
query_cache/types.h
query_cache.h query_cache.h
rasterizer_accelerated.cpp rasterizer_accelerated.cpp
rasterizer_accelerated.h rasterizer_accelerated.h

View File

@ -272,19 +272,13 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
if (!cpu_addr) { if (!cpu_addr) {
return {&slot_buffers[NULL_BUFFER_ID], 0}; return {&slot_buffers[NULL_BUFFER_ID], 0};
} }
return ObtainCPUBuffer(*cpu_addr, size, sync_info, post_op); const BufferId buffer_id = FindBuffer(*cpu_addr, size);
}
template <class P>
std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
VAddr cpu_addr, u32 size, ObtainBufferSynchronize sync_info, ObtainBufferOperation post_op) {
const BufferId buffer_id = FindBuffer(cpu_addr, size);
Buffer& buffer = slot_buffers[buffer_id]; Buffer& buffer = slot_buffers[buffer_id];
// synchronize op // synchronize op
switch (sync_info) { switch (sync_info) {
case ObtainBufferSynchronize::FullSynchronize: case ObtainBufferSynchronize::FullSynchronize:
SynchronizeBuffer(buffer, cpu_addr, size); SynchronizeBuffer(buffer, *cpu_addr, size);
break; break;
default: default:
break; break;
@ -292,11 +286,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
switch (post_op) { switch (post_op) {
case ObtainBufferOperation::MarkAsWritten: case ObtainBufferOperation::MarkAsWritten:
MarkWrittenBuffer(buffer_id, cpu_addr, size); MarkWrittenBuffer(buffer_id, *cpu_addr, size);
break; break;
case ObtainBufferOperation::DiscardWrite: { case ObtainBufferOperation::DiscardWrite: {
VAddr cpu_addr_start = Common::AlignDown(cpu_addr, 64); VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
VAddr cpu_addr_end = Common::AlignUp(cpu_addr + size, 64); VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
IntervalType interval{cpu_addr_start, cpu_addr_end}; IntervalType interval{cpu_addr_start, cpu_addr_end};
ClearDownload(interval); ClearDownload(interval);
common_ranges.subtract(interval); common_ranges.subtract(interval);
@ -306,7 +300,7 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
break; break;
} }
return {&buffer, buffer.Offset(cpu_addr)}; return {&buffer, buffer.Offset(*cpu_addr)};
} }
template <class P> template <class P>

View File

@ -295,10 +295,6 @@ public:
[[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size, [[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
ObtainBufferSynchronize sync_info, ObtainBufferSynchronize sync_info,
ObtainBufferOperation post_op); ObtainBufferOperation post_op);
[[nodiscard]] std::pair<Buffer*, u32> ObtainCPUBuffer(VAddr gpu_addr, u32 size,
ObtainBufferSynchronize sync_info,
ObtainBufferOperation post_op);
void FlushCachedWrites(); void FlushCachedWrites();
/// Return true when there are uncommitted buffers to be downloaded /// Return true when there are uncommitted buffers to be downloaded
@ -339,14 +335,6 @@ public:
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer(); [[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
template <typename Func>
void BufferOperations(Func&& func) {
do {
channel_state->has_deleted_buffers = false;
func();
} while (channel_state->has_deleted_buffers);
}
std::recursive_mutex mutex; std::recursive_mutex mutex;
Runtime& runtime; Runtime& runtime;

View File

@ -51,7 +51,7 @@ public:
virtual void CreateChannel(Tegra::Control::ChannelState& channel); virtual void CreateChannel(Tegra::Control::ChannelState& channel);
/// Bind a channel for execution. /// Bind a channel for execution.
virtual void BindToChannel(s32 id); void BindToChannel(s32 id);
/// Erase channel's state. /// Erase channel's state.
void EraseChannel(s32 id); void EraseChannel(s32 id);

View File

@ -46,7 +46,6 @@ public:
}; };
struct IndirectParams { struct IndirectParams {
bool is_byte_count;
bool is_indexed; bool is_indexed;
bool include_count; bool include_count;
GPUVAddr count_start_address; GPUVAddr count_start_address;

View File

@ -20,6 +20,8 @@
namespace Tegra::Engines { namespace Tegra::Engines {
using VideoCore::QueryType;
/// First register id that is actually a Macro call. /// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00; constexpr u32 MacroRegistersStart = 0xE00;
@ -498,21 +500,27 @@ void Maxwell3D::StampQueryResult(u64 payload, bool long_query) {
} }
void Maxwell3D::ProcessQueryGet() { void Maxwell3D::ProcessQueryGet() {
VideoCommon::QueryPropertiesFlags flags{};
if (regs.report_semaphore.query.short_query == 0) {
flags |= VideoCommon::QueryPropertiesFlags::HasTimeout;
}
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
const VideoCommon::QueryType query_type =
static_cast<VideoCommon::QueryType>(regs.report_semaphore.query.report.Value());
const u32 payload = regs.report_semaphore.payload;
const u32 subreport = regs.report_semaphore.query.sub_report;
switch (regs.report_semaphore.query.operation) { switch (regs.report_semaphore.query.operation) {
case Regs::ReportSemaphore::Operation::Release: case Regs::ReportSemaphore::Operation::Release:
if (regs.report_semaphore.query.short_query != 0) { if (regs.report_semaphore.query.short_query != 0) {
flags |= VideoCommon::QueryPropertiesFlags::IsAFence; const GPUVAddr sequence_address{regs.report_semaphore.Address()};
const u32 payload = regs.report_semaphore.payload;
std::function<void()> operation([this, sequence_address, payload] {
memory_manager.Write<u32>(sequence_address, payload);
});
rasterizer->SignalFence(std::move(operation));
} else {
struct LongQueryResult {
u64_le value;
u64_le timestamp;
};
const GPUVAddr sequence_address{regs.report_semaphore.Address()};
const u32 payload = regs.report_semaphore.payload;
[this, sequence_address, payload] {
memory_manager.Write<u64>(sequence_address + sizeof(u64), system.GPU().GetTicks());
memory_manager.Write<u64>(sequence_address, payload);
}();
} }
rasterizer->Query(sequence_address, query_type, flags, payload, subreport);
break; break;
case Regs::ReportSemaphore::Operation::Acquire: case Regs::ReportSemaphore::Operation::Acquire:
// TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
@ -520,7 +528,11 @@ void Maxwell3D::ProcessQueryGet() {
UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE"); UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
break; break;
case Regs::ReportSemaphore::Operation::ReportOnly: case Regs::ReportSemaphore::Operation::ReportOnly:
rasterizer->Query(sequence_address, query_type, flags, payload, subreport); if (const std::optional<u64> result = GetQueryResult()) {
// If the query returns an empty optional it means it's cached and deferred.
// In this case we have a non-empty result, so we stamp it immediately.
StampQueryResult(*result, regs.report_semaphore.query.short_query == 0);
}
break; break;
case Regs::ReportSemaphore::Operation::Trap: case Regs::ReportSemaphore::Operation::Trap:
UNIMPLEMENTED_MSG("Unimplemented query operation TRAP"); UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
@ -532,10 +544,6 @@ void Maxwell3D::ProcessQueryGet() {
} }
void Maxwell3D::ProcessQueryCondition() { void Maxwell3D::ProcessQueryCondition() {
if (rasterizer->AccelerateConditionalRendering()) {
execute_on = true;
return;
}
const GPUVAddr condition_address{regs.render_enable.Address()}; const GPUVAddr condition_address{regs.render_enable.Address()};
switch (regs.render_enable_override) { switch (regs.render_enable_override) {
case Regs::RenderEnable::Override::AlwaysRender: case Regs::RenderEnable::Override::AlwaysRender:
@ -545,6 +553,10 @@ void Maxwell3D::ProcessQueryCondition() {
execute_on = false; execute_on = false;
break; break;
case Regs::RenderEnable::Override::UseRenderEnable: { case Regs::RenderEnable::Override::UseRenderEnable: {
if (rasterizer->AccelerateConditionalRendering()) {
execute_on = true;
return;
}
switch (regs.render_enable.mode) { switch (regs.render_enable.mode) {
case Regs::RenderEnable::Mode::True: { case Regs::RenderEnable::Mode::True: {
execute_on = true; execute_on = true;
@ -586,9 +598,15 @@ void Maxwell3D::ProcessQueryCondition() {
} }
void Maxwell3D::ProcessCounterReset() { void Maxwell3D::ProcessCounterReset() {
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return;
}
#endif
switch (regs.clear_report_value) { switch (regs.clear_report_value) {
case Regs::ClearReport::ZPassPixelCount: case Regs::ClearReport::ZPassPixelCount:
rasterizer->ResetCounter(VideoCommon::QueryType::ZPassPixelCount64); rasterizer->ResetCounter(QueryType::SamplesPassed);
break; break;
default: default:
LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value); LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value);
@ -602,6 +620,28 @@ void Maxwell3D::ProcessSyncPoint() {
rasterizer->SignalSyncPoint(sync_point); rasterizer->SignalSyncPoint(sync_point);
} }
std::optional<u64> Maxwell3D::GetQueryResult() {
switch (regs.report_semaphore.query.report) {
case Regs::ReportSemaphore::Report::Payload:
return regs.report_semaphore.payload;
case Regs::ReportSemaphore::Report::ZPassPixelCount64:
#if ANDROID
if (!Settings::IsGPULevelHigh()) {
// This is problematic on Android, disable on GPU Normal.
return 120;
}
#endif
// Deferred.
rasterizer->Query(regs.report_semaphore.Address(), QueryType::SamplesPassed,
system.GPU().GetTicks());
return std::nullopt;
default:
LOG_DEBUG(HW_GPU, "Unimplemented query report type {}",
regs.report_semaphore.query.report.Value());
return 1;
}
}
void Maxwell3D::ProcessCBBind(size_t stage_index) { void Maxwell3D::ProcessCBBind(size_t stage_index) {
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader
// stage. // stage.

View File

@ -3182,6 +3182,9 @@ private:
/// Handles writes to syncing register. /// Handles writes to syncing register.
void ProcessSyncPoint(); void ProcessSyncPoint();
/// Returns a query's value or an empty object if the value will be deferred through a cache.
std::optional<u64> GetQueryResult();
void RefreshParametersImpl(); void RefreshParametersImpl();
bool IsMethodExecutable(u32 method); bool IsMethodExecutable(u32 method);

View File

@ -109,11 +109,10 @@ void MaxwellDMA::Launch() {
const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) { if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
ASSERT(regs.remap_const.component_size_minus_one == 3); ASSERT(regs.remap_const.component_size_minus_one == 3);
accelerate.BufferClear(regs.offset_out, regs.line_length_in, accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
regs.remap_const.remap_consta_value);
read_buffer.resize_destructive(regs.line_length_in * sizeof(u32)); read_buffer.resize_destructive(regs.line_length_in * sizeof(u32));
std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in); std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in);
std::ranges::fill(span, regs.remap_const.remap_consta_value); std::ranges::fill(span, regs.remap_consta_value);
memory_manager.WriteBlockUnsafe(regs.offset_out, memory_manager.WriteBlockUnsafe(regs.offset_out,
reinterpret_cast<u8*>(read_buffer.data()), reinterpret_cast<u8*>(read_buffer.data()),
regs.line_length_in * sizeof(u32)); regs.line_length_in * sizeof(u32));
@ -362,17 +361,21 @@ void MaxwellDMA::ReleaseSemaphore() {
const auto type = regs.launch_dma.semaphore_type; const auto type = regs.launch_dma.semaphore_type;
const GPUVAddr address = regs.semaphore.address; const GPUVAddr address = regs.semaphore.address;
const u32 payload = regs.semaphore.payload; const u32 payload = regs.semaphore.payload;
VideoCommon::QueryPropertiesFlags flags{VideoCommon::QueryPropertiesFlags::IsAFence};
switch (type) { switch (type) {
case LaunchDMA::SemaphoreType::NONE: case LaunchDMA::SemaphoreType::NONE:
break; break;
case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: { case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: {
rasterizer->Query(address, VideoCommon::QueryType::Payload, flags, payload, 0); std::function<void()> operation(
[this, address, payload] { memory_manager.Write<u32>(address, payload); });
rasterizer->SignalFence(std::move(operation));
break; break;
} }
case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: { case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: {
rasterizer->Query(address, VideoCommon::QueryType::Payload, std::function<void()> operation([this, address, payload] {
flags | VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0); memory_manager.Write<u64>(address + sizeof(u64), system.GPU().GetTicks());
memory_manager.Write<u64>(address, payload);
});
rasterizer->SignalFence(std::move(operation));
break; break;
} }
default: default:

View File

@ -214,15 +214,14 @@ public:
NO_WRITE = 6, NO_WRITE = 6,
}; };
u32 remap_consta_value; PackedGPUVAddr address;
u32 remap_constb_value;
union { union {
BitField<0, 12, u32> dst_components_raw;
BitField<0, 3, Swizzle> dst_x; BitField<0, 3, Swizzle> dst_x;
BitField<4, 3, Swizzle> dst_y; BitField<4, 3, Swizzle> dst_y;
BitField<8, 3, Swizzle> dst_z; BitField<8, 3, Swizzle> dst_z;
BitField<12, 3, Swizzle> dst_w; BitField<12, 3, Swizzle> dst_w;
BitField<0, 12, u32> dst_components_raw;
BitField<16, 2, u32> component_size_minus_one; BitField<16, 2, u32> component_size_minus_one;
BitField<20, 2, u32> num_src_components_minus_one; BitField<20, 2, u32> num_src_components_minus_one;
BitField<24, 2, u32> num_dst_components_minus_one; BitField<24, 2, u32> num_dst_components_minus_one;
@ -275,57 +274,55 @@ private:
struct Regs { struct Regs {
union { union {
struct { struct {
INSERT_PADDING_BYTES_NOINIT(0x100); u32 reserved[0x40];
u32 nop; u32 nop;
INSERT_PADDING_BYTES_NOINIT(0x3C); u32 reserved01[0xf];
u32 pm_trigger; u32 pm_trigger;
INSERT_PADDING_BYTES_NOINIT(0xFC); u32 reserved02[0x3f];
Semaphore semaphore; Semaphore semaphore;
INSERT_PADDING_BYTES_NOINIT(0x8); u32 reserved03[0x2];
RenderEnable render_enable; RenderEnable render_enable;
PhysMode src_phys_mode; PhysMode src_phys_mode;
PhysMode dst_phys_mode; PhysMode dst_phys_mode;
INSERT_PADDING_BYTES_NOINIT(0x98); u32 reserved04[0x26];
LaunchDMA launch_dma; LaunchDMA launch_dma;
INSERT_PADDING_BYTES_NOINIT(0xFC); u32 reserved05[0x3f];
PackedGPUVAddr offset_in; PackedGPUVAddr offset_in;
PackedGPUVAddr offset_out; PackedGPUVAddr offset_out;
s32 pitch_in; s32 pitch_in;
s32 pitch_out; s32 pitch_out;
u32 line_length_in; u32 line_length_in;
u32 line_count; u32 line_count;
INSERT_PADDING_BYTES_NOINIT(0x2E0); u32 reserved06[0xb6];
u32 remap_consta_value;
u32 remap_constb_value;
RemapConst remap_const; RemapConst remap_const;
DMA::Parameters dst_params; DMA::Parameters dst_params;
INSERT_PADDING_BYTES_NOINIT(0x4); u32 reserved07[0x1];
DMA::Parameters src_params; DMA::Parameters src_params;
INSERT_PADDING_BYTES_NOINIT(0x9D4); u32 reserved08[0x275];
u32 pm_trigger_end; u32 pm_trigger_end;
INSERT_PADDING_BYTES_NOINIT(0xEE8); u32 reserved09[0x3ba];
}; };
std::array<u32, NUM_REGS> reg_array; std::array<u32, NUM_REGS> reg_array;
}; };
} regs{}; } regs{};
static_assert(sizeof(Regs) == NUM_REGS * 4);
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position, \ static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position") "Field " #field_name " has invalid position")
ASSERT_REG_POSITION(semaphore, 0x240); ASSERT_REG_POSITION(launch_dma, 0xC0);
ASSERT_REG_POSITION(render_enable, 0x254); ASSERT_REG_POSITION(offset_in, 0x100);
ASSERT_REG_POSITION(src_phys_mode, 0x260); ASSERT_REG_POSITION(offset_out, 0x102);
ASSERT_REG_POSITION(launch_dma, 0x300); ASSERT_REG_POSITION(pitch_in, 0x104);
ASSERT_REG_POSITION(offset_in, 0x400); ASSERT_REG_POSITION(pitch_out, 0x105);
ASSERT_REG_POSITION(offset_out, 0x408); ASSERT_REG_POSITION(line_length_in, 0x106);
ASSERT_REG_POSITION(pitch_in, 0x410); ASSERT_REG_POSITION(line_count, 0x107);
ASSERT_REG_POSITION(pitch_out, 0x414); ASSERT_REG_POSITION(remap_const, 0x1C0);
ASSERT_REG_POSITION(line_length_in, 0x418); ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(line_count, 0x41C); ASSERT_REG_POSITION(src_params, 0x1CA);
ASSERT_REG_POSITION(remap_const, 0x700);
ASSERT_REG_POSITION(dst_params, 0x70C);
ASSERT_REG_POSITION(src_params, 0x728);
ASSERT_REG_POSITION(pm_trigger_end, 0x1114);
#undef ASSERT_REG_POSITION #undef ASSERT_REG_POSITION
}; };

View File

@ -82,8 +82,10 @@ void Puller::ProcessSemaphoreTriggerMethod() {
if (op == GpuSemaphoreOperation::WriteLong) { if (op == GpuSemaphoreOperation::WriteLong) {
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
const u32 payload = regs.semaphore_sequence; const u32 payload = regs.semaphore_sequence;
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, [this, sequence_address, payload] {
VideoCommon::QueryPropertiesFlags::HasTimeout, payload, 0); memory_manager.Write<u64>(sequence_address + sizeof(u64), gpu.GetTicks());
memory_manager.Write<u64>(sequence_address, payload);
}();
} else { } else {
do { do {
const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())}; const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())};
@ -118,8 +120,10 @@ void Puller::ProcessSemaphoreTriggerMethod() {
void Puller::ProcessSemaphoreRelease() { void Puller::ProcessSemaphoreRelease() {
const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()}; const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
const u32 payload = regs.semaphore_release; const u32 payload = regs.semaphore_release;
rasterizer->Query(sequence_address, VideoCommon::QueryType::Payload, std::function<void()> operation([this, sequence_address, payload] {
VideoCommon::QueryPropertiesFlags::IsAFence, payload, 0); memory_manager.Write<u32>(sequence_address, payload);
});
rasterizer->SignalFence(std::move(operation));
} }
void Puller::ProcessSemaphoreAcquire() { void Puller::ProcessSemaphoreAcquire() {
@ -128,6 +132,7 @@ void Puller::ProcessSemaphoreAcquire() {
while (word != value) { while (word != value) {
regs.acquire_active = true; regs.acquire_active = true;
regs.acquire_value = value; regs.acquire_value = value;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
rasterizer->ReleaseFences(); rasterizer->ReleaseFences();
word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress()); word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
// TODO(kemathe73) figure out how to do the acquire_timeout // TODO(kemathe73) figure out how to do the acquire_timeout

View File

@ -55,9 +55,6 @@ public:
// Unlike other fences, this one doesn't // Unlike other fences, this one doesn't
void SignalOrdering() { void SignalOrdering() {
if constexpr (!can_async_check) {
TryReleasePendingFences<false>();
}
std::scoped_lock lock{buffer_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.AccumulateFlushes(); buffer_cache.AccumulateFlushes();
} }
@ -107,25 +104,9 @@ public:
SignalFence(std::move(func)); SignalFence(std::move(func));
} }
void WaitPendingFences([[maybe_unused]] bool force) { void WaitPendingFences() {
if constexpr (!can_async_check) { if constexpr (!can_async_check) {
TryReleasePendingFences<true>(); TryReleasePendingFences<true>();
} else {
if (!force) {
return;
}
std::mutex wait_mutex;
std::condition_variable wait_cv;
std::atomic<bool> wait_finished{};
std::function<void()> func([&] {
std::scoped_lock lk(wait_mutex);
wait_finished.store(true, std::memory_order_relaxed);
wait_cv.notify_all();
});
SignalFence(std::move(func));
std::unique_lock lk(wait_mutex);
wait_cv.wait(
lk, [&wait_finished] { return wait_finished.load(std::memory_order_relaxed); });
} }
} }

View File

@ -102,8 +102,7 @@ struct GPU::Impl {
/// Signal the ending of command list. /// Signal the ending of command list.
void OnCommandListEnd() { void OnCommandListEnd() {
rasterizer->ReleaseFences(false); rasterizer->ReleaseFences();
Settings::UpdateGPUAccuracy();
} }
/// Request a host GPU memory flush from the CPU. /// Request a host GPU memory flush from the CPU.
@ -221,7 +220,6 @@ struct GPU::Impl {
/// This can be used to launch any necessary threads and register any necessary /// This can be used to launch any necessary threads and register any necessary
/// core timing events. /// core timing events.
void Start() { void Start() {
Settings::UpdateGPUAccuracy();
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
} }

View File

@ -41,9 +41,6 @@ set(SHADER_FILES
pitch_unswizzle.comp pitch_unswizzle.comp
present_bicubic.frag present_bicubic.frag
present_gaussian.frag present_gaussian.frag
queries_prefix_scan_sum.comp
queries_prefix_scan_sum_nosubgroups.comp
resolve_conditional_render.comp
smaa_edge_detection.vert smaa_edge_detection.vert
smaa_edge_detection.frag smaa_edge_detection.frag
smaa_blending_weight_calculation.vert smaa_blending_weight_calculation.vert
@ -73,7 +70,6 @@ if ("${GLSLANGVALIDATOR}" STREQUAL "GLSLANGVALIDATOR-NOTFOUND")
endif() endif()
set(GLSL_FLAGS "") set(GLSL_FLAGS "")
set(SPIR_V_VERSION "spirv1.3")
set(QUIET_FLAG "--quiet") set(QUIET_FLAG "--quiet")
set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
@ -127,7 +123,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
OUTPUT OUTPUT
${SPIRV_HEADER_FILE} ${SPIRV_HEADER_FILE}
COMMAND COMMAND
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} --target-env ${SPIR_V_VERSION} ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE}
MAIN_DEPENDENCY MAIN_DEPENDENCY
${SOURCE_FILE} ${SOURCE_FILE}
) )

View File

@ -15,14 +15,11 @@ void main() {
// TODO: Specialization constants for num_samples? // TODO: Specialization constants for num_samples?
const int num_samples = imageSamples(msaa_in); const int num_samples = imageSamples(msaa_in);
const ivec3 msaa_size = imageSize(msaa_in);
const ivec3 out_size = imageSize(output_img);
const ivec3 scale = out_size / msaa_size;
for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) { for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) {
const vec4 pixel = imageLoad(msaa_in, coords, curr_sample); const vec4 pixel = imageLoad(msaa_in, coords, curr_sample);
const int single_sample_x = scale.x * coords.x + (curr_sample & 1); const int single_sample_x = 2 * coords.x + (curr_sample & 1);
const int single_sample_y = scale.y * coords.y + ((curr_sample / 2) & 1); const int single_sample_y = 2 * coords.y + ((curr_sample / 2) & 1);
const ivec3 dest_coords = ivec3(single_sample_x, single_sample_y, coords.z); const ivec3 dest_coords = ivec3(single_sample_x, single_sample_y, coords.z);
if (any(greaterThanEqual(dest_coords, imageSize(output_img)))) { if (any(greaterThanEqual(dest_coords, imageSize(output_img)))) {

View File

@ -15,12 +15,9 @@ void main() {
// TODO: Specialization constants for num_samples? // TODO: Specialization constants for num_samples?
const int num_samples = imageSamples(output_msaa); const int num_samples = imageSamples(output_msaa);
const ivec3 msaa_size = imageSize(output_msaa);
const ivec3 out_size = imageSize(img_in);
const ivec3 scale = out_size / msaa_size;
for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) { for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) {
const int single_sample_x = scale.x * coords.x + (curr_sample & 1); const int single_sample_x = 2 * coords.x + (curr_sample & 1);
const int single_sample_y = scale.y * coords.y + ((curr_sample / 2) & 1); const int single_sample_y = 2 * coords.y + ((curr_sample / 2) & 1);
const ivec3 single_coords = ivec3(single_sample_x, single_sample_y, coords.z); const ivec3 single_coords = ivec3(single_sample_x, single_sample_y, coords.z);
if (any(greaterThanEqual(single_coords, imageSize(img_in)))) { if (any(greaterThanEqual(single_coords, imageSize(img_in)))) {

View File

@ -1,173 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 460 core
#extension GL_KHR_shader_subgroup_basic : require
#extension GL_KHR_shader_subgroup_shuffle : require
#extension GL_KHR_shader_subgroup_shuffle_relative : require
#extension GL_KHR_shader_subgroup_arithmetic : require
#ifdef VULKAN
#define HAS_EXTENDED_TYPES 1
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#extension GL_NV_gpu_shader5 : enable
#ifdef GL_NV_gpu_shader5
#define HAS_EXTENDED_TYPES 1
#else
#define HAS_EXTENDED_TYPES 0
#endif
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 0
#endif
BEGIN_PUSH_CONSTANTS
UNIFORM(0) uint min_accumulation_base;
UNIFORM(1) uint max_accumulation_base;
UNIFORM(2) uint accumulation_limit;
UNIFORM(3) uint buffer_offset;
END_PUSH_CONSTANTS
#define LOCAL_RESULTS 8
#define QUERIES_PER_INVOC 2048
layout(local_size_x = QUERIES_PER_INVOC / LOCAL_RESULTS) in;
layout(std430, binding = 0) readonly buffer block1 {
uvec2 input_data[];
};
layout(std430, binding = 1) coherent buffer block2 {
uvec2 output_data[];
};
layout(std430, binding = 2) coherent buffer block3 {
uvec2 accumulated_data;
};
shared uvec2 shared_data[128];
// Simple Uint64 add that uses 2 uint variables for GPUs that don't support uint64
uvec2 AddUint64(uvec2 value_1, uvec2 value_2) {
uint carry = 0;
uvec2 result;
result.x = uaddCarry(value_1.x, value_2.x, carry);
result.y = value_1.y + value_2.y + carry;
return result;
}
// do subgroup Prefix Sum using Hillis and Steele's algorithm
uvec2 subgroupInclusiveAddUint64(uvec2 value) {
uvec2 result = value;
for (uint i = 1; i < gl_SubgroupSize; i *= 2) {
uvec2 other = subgroupShuffleUp(result, i); // get value from subgroup_inv_id - i;
if (i <= gl_SubgroupInvocationID) {
result = AddUint64(result, other);
}
}
return result;
}
// Writes down the results to the output buffer and to the accumulation buffer
void WriteResults(uvec2 results[LOCAL_RESULTS]) {
const uint current_id = gl_LocalInvocationID.x;
const uvec2 accum = accumulated_data;
for (uint i = 0; i < LOCAL_RESULTS; i++) {
uvec2 base_data = current_id * LOCAL_RESULTS + i < min_accumulation_base ? accum : uvec2(0, 0);
AddUint64(results[i], base_data);
}
for (uint i = 0; i < LOCAL_RESULTS; i++) {
output_data[buffer_offset + current_id * LOCAL_RESULTS + i] = results[i];
}
uint index = accumulation_limit % LOCAL_RESULTS;
uint base_id = accumulation_limit / LOCAL_RESULTS;
if (min_accumulation_base >= accumulation_limit + 1) {
if (current_id == base_id) {
accumulated_data = results[index];
}
return;
}
// We have that ugly case in which the accumulation data is reset in the middle somewhere.
barrier();
groupMemoryBarrier();
if (current_id == base_id) {
uvec2 reset_value = output_data[max_accumulation_base - 1];
// Calculate two complement / negate manually
reset_value = AddUint64(uvec2(1,0), ~reset_value);
accumulated_data = AddUint64(results[index], reset_value);
}
}
void main() {
const uint subgroup_inv_id = gl_SubgroupInvocationID;
const uint subgroup_id = gl_SubgroupID + gl_WorkGroupID.x * gl_NumSubgroups;
const uint last_subgroup_id = subgroupMax(subgroup_inv_id);
const uint current_id = gl_LocalInvocationID.x;
const uint total_work = accumulation_limit;
const uint last_result_id = LOCAL_RESULTS - 1;
uvec2 data[LOCAL_RESULTS];
for (uint i = 0; i < LOCAL_RESULTS; i++) {
data[i] = input_data[buffer_offset + current_id * LOCAL_RESULTS + i];
}
uvec2 results[LOCAL_RESULTS];
results[0] = data[0];
for (uint i = 1; i < LOCAL_RESULTS; i++) {
results[i] = AddUint64(data[i], results[i - 1]);
}
// make sure all input data has been loaded
subgroupBarrier();
subgroupMemoryBarrier();
// on the last local result, do a subgroup inclusive scan sum
results[last_result_id] = subgroupInclusiveAddUint64(results[last_result_id]);
// get the last local result from the subgroup behind the current
uvec2 result_behind = subgroupShuffleUp(results[last_result_id], 1);
if (subgroup_inv_id != 0) {
for (uint i = 1; i < LOCAL_RESULTS; i++) {
results[i - 1] = AddUint64(results[i - 1], result_behind);
}
}
// if we had less queries than our subgroup, just write down the results.
if (total_work <= gl_SubgroupSize * LOCAL_RESULTS) { // This condition is constant per dispatch.
WriteResults(results);
return;
}
// We now have more, so lets write the last result into shared memory.
// Only pick the last subgroup.
if (subgroup_inv_id == last_subgroup_id) {
shared_data[subgroup_id] = results[last_result_id];
}
// wait until everyone loaded their stuffs
barrier();
memoryBarrierShared();
// only if it's not the first subgroup
if (subgroup_id != 0) {
// get the results from some previous invocation
uvec2 tmp = shared_data[subgroup_inv_id];
subgroupBarrier();
subgroupMemoryBarrierShared();
tmp = subgroupInclusiveAddUint64(tmp);
// obtain the result that would be equivalent to the previous result
uvec2 shuffled_result = subgroupShuffle(tmp, subgroup_id - 1);
for (uint i = 0; i < LOCAL_RESULTS; i++) {
results[i] = AddUint64(results[i], shuffled_result);
}
}
WriteResults(results);
}

View File

@ -1,138 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2015 Graham Sellers, Richard Wright Jr. and Nicholas Haemel
// SPDX-License-Identifier: MIT
// Code obtained from OpenGL SuperBible, Seventh Edition by Graham Sellers, Richard Wright Jr. and
// Nicholas Haemel. Modified to suit needs.
#version 460 core
#ifdef VULKAN
#define HAS_EXTENDED_TYPES 1
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
#extension GL_NV_gpu_shader5 : enable
#ifdef GL_NV_gpu_shader5
#define HAS_EXTENDED_TYPES 1
#else
#define HAS_EXTENDED_TYPES 0
#endif
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform
#define BINDING_INPUT_BUFFER 0
#define BINDING_OUTPUT_IMAGE 0
#endif
BEGIN_PUSH_CONSTANTS
UNIFORM(0) uint min_accumulation_base;
UNIFORM(1) uint max_accumulation_base;
UNIFORM(2) uint accumulation_limit;
UNIFORM(3) uint buffer_offset;
END_PUSH_CONSTANTS
#define LOCAL_RESULTS 4
#define QUERIES_PER_INVOC 2048
layout(local_size_x = QUERIES_PER_INVOC / LOCAL_RESULTS) in;
layout(std430, binding = 0) readonly buffer block1 {
uvec2 input_data[gl_WorkGroupSize.x * LOCAL_RESULTS];
};
layout(std430, binding = 1) writeonly coherent buffer block2 {
uvec2 output_data[gl_WorkGroupSize.x * LOCAL_RESULTS];
};
layout(std430, binding = 2) coherent buffer block3 {
uvec2 accumulated_data;
};
shared uvec2 shared_data[gl_WorkGroupSize.x * LOCAL_RESULTS];
uvec2 AddUint64(uvec2 value_1, uvec2 value_2) {
uint carry = 0;
uvec2 result;
result.x = uaddCarry(value_1.x, value_2.x, carry);
result.y = value_1.y + value_2.y + carry;
return result;
}
void main(void) {
uint id = gl_LocalInvocationID.x;
uvec2 base_value[LOCAL_RESULTS];
const uvec2 accum = accumulated_data;
for (uint i = 0; i < LOCAL_RESULTS; i++) {
base_value[i] = (buffer_offset + id * LOCAL_RESULTS + i) < min_accumulation_base
? accumulated_data
: uvec2(0);
}
uint work_size = gl_WorkGroupSize.x;
uint rd_id;
uint wr_id;
uint mask;
uvec2 inputs[LOCAL_RESULTS];
for (uint i = 0; i < LOCAL_RESULTS; i++) {
inputs[i] = input_data[buffer_offset + id * LOCAL_RESULTS + i];
}
// The number of steps is the log base 2 of the
// work group size, which should be a power of 2
const uint steps = uint(log2(work_size)) + uint(log2(LOCAL_RESULTS));
uint step = 0;
// Each invocation is responsible for the content of
// two elements of the output array
for (uint i = 0; i < LOCAL_RESULTS; i++) {
shared_data[id * LOCAL_RESULTS + i] = inputs[i];
}
// Synchronize to make sure that everyone has initialized
// their elements of shared_data[] with data loaded from
// the input arrays
barrier();
memoryBarrierShared();
// For each step...
for (step = 0; step < steps; step++) {
// Calculate the read and write index in the
// shared array
mask = (1 << step) - 1;
rd_id = ((id >> step) << (step + 1)) + mask;
wr_id = rd_id + 1 + (id & mask);
// Accumulate the read data into our element
shared_data[wr_id] = AddUint64(shared_data[rd_id], shared_data[wr_id]);
// Synchronize again to make sure that everyone
// has caught up with us
barrier();
memoryBarrierShared();
}
// Add the accumulation
for (uint i = 0; i < LOCAL_RESULTS; i++) {
shared_data[id * LOCAL_RESULTS + i] =
AddUint64(shared_data[id * LOCAL_RESULTS + i], base_value[i]);
}
barrier();
memoryBarrierShared();
// Finally write our data back to the output buffer
for (uint i = 0; i < LOCAL_RESULTS; i++) {
output_data[buffer_offset + id * LOCAL_RESULTS + i] = shared_data[id * LOCAL_RESULTS + i];
}
if (id == 0) {
if (min_accumulation_base >= accumulation_limit + 1) {
accumulated_data = shared_data[accumulation_limit];
return;
}
uvec2 reset_value = shared_data[max_accumulation_base - 1];
uvec2 final_value = shared_data[accumulation_limit];
// Two complements
reset_value = AddUint64(uvec2(1, 0), ~reset_value);
accumulated_data = AddUint64(final_value, reset_value);
}
}

View File

@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#version 450
layout(local_size_x = 1) in;
layout(std430, binding = 0) buffer Query {
uvec2 initial;
uvec2 unknown;
uvec2 current;
};
layout(std430, binding = 1) buffer Result {
uint result;
};
void main() {
result = all(equal(initial, current)) ? 1 : 0;
}

View File

@ -67,7 +67,6 @@ public:
} }
auto& params = maxwell3d.draw_manager->GetIndirectParams(); auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = false;
params.is_indexed = false; params.is_indexed = false;
params.include_count = false; params.include_count = false;
params.count_start_address = 0; params.count_start_address = 0;
@ -162,7 +161,6 @@ public:
0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance); 0, 0x644, Maxwell3D::HLEReplacementAttributeType::BaseInstance);
} }
auto& params = maxwell3d.draw_manager->GetIndirectParams(); auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = false;
params.is_indexed = true; params.is_indexed = true;
params.include_count = false; params.include_count = false;
params.count_start_address = 0; params.count_start_address = 0;
@ -258,7 +256,6 @@ public:
const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize()); const u32 estimate = static_cast<u32>(maxwell3d.EstimateIndexBufferSize());
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
auto& params = maxwell3d.draw_manager->GetIndirectParams(); auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = false;
params.is_indexed = true; params.is_indexed = true;
params.include_count = true; params.include_count = true;
params.count_start_address = maxwell3d.GetMacroAddress(4); params.count_start_address = maxwell3d.GetMacroAddress(4);
@ -322,47 +319,6 @@ private:
} }
}; };
class HLE_DrawIndirectByteCount final : public HLEMacroImpl {
public:
explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU);
if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) {
Fallback(parameters);
return;
}
auto& params = maxwell3d.draw_manager->GetIndirectParams();
params.is_byte_count = true;
params.is_indexed = false;
params.include_count = false;
params.count_start_address = 0;
params.indirect_start_address = maxwell3d.GetMacroAddress(2);
params.buffer_size = 4;
params.max_draw_counts = 1;
params.stride = parameters[1];
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager->DrawArrayIndirect(topology);
}
private:
void Fallback(const std::vector<u32>& parameters) {
maxwell3d.RefreshParameters();
maxwell3d.regs.draw.begin = parameters[0];
maxwell3d.regs.draw_auto_stride = parameters[1];
maxwell3d.regs.draw_auto_byte_count = parameters[2];
maxwell3d.draw_manager->DrawArray(
maxwell3d.regs.draw.topology, 0,
maxwell3d.regs.draw_auto_byte_count / maxwell3d.regs.draw_auto_stride, 0, 1);
}
};
class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl { class HLE_C713C83D8F63CCF3 final : public HLEMacroImpl {
public: public:
explicit HLE_C713C83D8F63CCF3(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} explicit HLE_C713C83D8F63CCF3(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
@ -580,11 +536,6 @@ HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
[](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__); return std::make_unique<HLE_TransformFeedbackSetup>(maxwell3d__);
})); }));
builders.emplace(0xB5F74EDB717278ECULL,
std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
[](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
return std::make_unique<HLE_DrawIndirectByteCount>(maxwell3d__);
}));
} }
HLEMacro::~HLEMacro() = default; HLEMacro::~HLEMacro() = default;

View File

@ -25,13 +25,6 @@
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/texture_cache/slot_vector.h" #include "video_core/texture_cache/slot_vector.h"
namespace VideoCore {
enum class QueryType {
SamplesPassed,
};
constexpr std::size_t NumQueryTypes = 1;
} // namespace VideoCore
namespace VideoCommon { namespace VideoCommon {
using AsyncJobId = SlotId; using AsyncJobId = SlotId;
@ -105,10 +98,10 @@ private:
}; };
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter> template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
class QueryCacheLegacy : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
public: public:
explicit QueryCacheLegacy(VideoCore::RasterizerInterface& rasterizer_, explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_) Core::Memory::Memory& cpu_memory_)
: rasterizer{rasterizer_}, : rasterizer{rasterizer_},
// Use reinterpret_cast instead of static_cast as workaround for // Use reinterpret_cast instead of static_cast as workaround for
// UBSan bug (https://github.com/llvm/llvm-project/issues/59060) // UBSan bug (https://github.com/llvm/llvm-project/issues/59060)

View File

@ -1,104 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <deque>
#include <utility>
#include "common/common_types.h"
namespace VideoCommon {
class BankBase {
protected:
const size_t base_bank_size{};
size_t bank_size{};
std::atomic<size_t> references{};
size_t current_slot{};
public:
explicit BankBase(size_t bank_size_) : base_bank_size{bank_size_}, bank_size(bank_size_) {}
virtual ~BankBase() = default;
virtual std::pair<bool, size_t> Reserve() {
if (IsClosed()) {
return {false, bank_size};
}
const size_t result = current_slot++;
return {true, result};
}
virtual void Reset() {
current_slot = 0;
references = 0;
bank_size = base_bank_size;
}
size_t Size() const {
return bank_size;
}
void AddReference(size_t how_many = 1) {
references.fetch_add(how_many, std::memory_order_relaxed);
}
void CloseReference(size_t how_many = 1) {
if (how_many > references.load(std::memory_order_relaxed)) {
UNREACHABLE();
}
references.fetch_sub(how_many, std::memory_order_relaxed);
}
void Close() {
bank_size = current_slot;
}
bool IsClosed() const {
return current_slot >= bank_size;
}
bool IsDead() const {
return IsClosed() && references == 0;
}
};
template <typename BankType>
class BankPool {
private:
std::deque<BankType> bank_pool;
std::deque<size_t> bank_indices;
public:
BankPool() = default;
~BankPool() = default;
// Reserve a bank from the pool and return its index
template <typename Func>
size_t ReserveBank(Func&& builder) {
if (!bank_indices.empty() && bank_pool[bank_indices.front()].IsDead()) {
size_t new_index = bank_indices.front();
bank_indices.pop_front();
bank_pool[new_index].Reset();
return new_index;
}
size_t new_index = bank_pool.size();
builder(bank_pool, new_index);
bank_indices.push_back(new_index);
return new_index;
}
// Get a reference to a bank using its index
BankType& GetBank(size_t index) {
return bank_pool[index];
}
// Get the total number of banks in the pool
size_t BankCount() const {
return bank_pool.size();
}
};
} // namespace VideoCommon

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace VideoCommon {
enum class QueryFlagBits : u32 {
HasTimestamp = 1 << 0, ///< Indicates if this query has a timestamp.
IsFinalValueSynced = 1 << 1, ///< Indicates if the query has been synced in the host
IsHostSynced = 1 << 2, ///< Indicates if the query has been synced in the host
IsGuestSynced = 1 << 3, ///< Indicates if the query has been synced with the guest.
IsHostManaged = 1 << 4, ///< Indicates if this query points to a host query
IsRewritten = 1 << 5, ///< Indicates if this query was rewritten by another query
IsInvalidated = 1 << 6, ///< Indicates the value of th query has been nullified.
IsOrphan = 1 << 7, ///< Indicates the query has not been set by a guest query.
IsFence = 1 << 8, ///< Indicates the query is a fence.
};
DECLARE_ENUM_FLAG_OPERATORS(QueryFlagBits)
class QueryBase {
public:
VAddr guest_address{};
QueryFlagBits flags{};
u64 value{};
protected:
// Default constructor
QueryBase() = default;
// Parameterized constructor
QueryBase(VAddr address, QueryFlagBits flags_, u64 value_)
: guest_address(address), flags(flags_), value{value_} {}
};
class GuestQuery : public QueryBase {
public:
// Parameterized constructor
GuestQuery(bool isLong, VAddr address, u64 queryValue)
: QueryBase(address, QueryFlagBits::IsFinalValueSynced, queryValue) {
if (isLong) {
flags |= QueryFlagBits::HasTimestamp;
}
}
};
class HostQueryBase : public QueryBase {
public:
// Default constructor
HostQueryBase() : QueryBase(0, QueryFlagBits::IsHostManaged | QueryFlagBits::IsOrphan, 0) {}
// Parameterized constructor
HostQueryBase(bool has_timestamp, VAddr address)
: QueryBase(address, QueryFlagBits::IsHostManaged, 0), start_bank_id{}, size_banks{},
start_slot{}, size_slots{} {
if (has_timestamp) {
flags |= QueryFlagBits::HasTimestamp;
}
}
u32 start_bank_id{};
u32 size_banks{};
size_t start_slot{};
size_t size_slots{};
};
} // namespace VideoCommon

View File

@ -1,580 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <deque>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/query_cache/bank_base.h"
#include "video_core/query_cache/query_base.h"
#include "video_core/query_cache/query_cache_base.h"
#include "video_core/query_cache/query_stream.h"
#include "video_core/query_cache/types.h"
namespace VideoCommon {
using Maxwell = Tegra::Engines::Maxwell3D;
struct SyncValuesStruct {
VAddr address;
u64 value;
u64 size;
static constexpr bool GeneratesBaseBuffer = true;
};
template <typename Traits>
class GuestStreamer : public SimpleStreamer<GuestQuery> {
public:
using RuntimeType = typename Traits::RuntimeType;
GuestStreamer(size_t id_, RuntimeType& runtime_)
: SimpleStreamer<GuestQuery>(id_), runtime{runtime_} {}
virtual ~GuestStreamer() = default;
size_t WriteCounter(VAddr address, bool has_timestamp, u32 value,
std::optional<u32> subreport = std::nullopt) override {
auto new_id = BuildQuery(has_timestamp, address, static_cast<u64>(value));
pending_sync.push_back(new_id);
return new_id;
}
bool HasPendingSync() const override {
return !pending_sync.empty();
}
void SyncWrites() override {
if (pending_sync.empty()) {
return;
}
std::vector<SyncValuesStruct> sync_values;
sync_values.reserve(pending_sync.size());
for (size_t pending_id : pending_sync) {
auto& query = slot_queries[pending_id];
if (True(query.flags & QueryFlagBits::IsRewritten) ||
True(query.flags & QueryFlagBits::IsInvalidated)) {
continue;
}
query.flags |= QueryFlagBits::IsHostSynced;
sync_values.emplace_back(SyncValuesStruct{
.address = query.guest_address,
.value = query.value,
.size = static_cast<u64>(True(query.flags & QueryFlagBits::HasTimestamp) ? 8 : 4)});
}
pending_sync.clear();
if (sync_values.size() > 0) {
runtime.template SyncValues<SyncValuesStruct>(sync_values);
}
}
private:
RuntimeType& runtime;
std::deque<size_t> pending_sync;
};
template <typename Traits>
class StubStreamer : public GuestStreamer<Traits> {
public:
using RuntimeType = typename Traits::RuntimeType;
StubStreamer(size_t id_, RuntimeType& runtime_, u32 stub_value_)
: GuestStreamer<Traits>(id_, runtime_), stub_value{stub_value_} {}
~StubStreamer() override = default;
size_t WriteCounter(VAddr address, bool has_timestamp, [[maybe_unused]] u32 value,
std::optional<u32> subreport = std::nullopt) override {
size_t new_id =
GuestStreamer<Traits>::WriteCounter(address, has_timestamp, stub_value, subreport);
return new_id;
}
private:
u32 stub_value;
};
template <typename Traits>
struct QueryCacheBase<Traits>::QueryCacheBaseImpl {
using RuntimeType = typename Traits::RuntimeType;
QueryCacheBaseImpl(QueryCacheBase<Traits>* owner_, VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, RuntimeType& runtime_, Tegra::GPU& gpu_)
: owner{owner_}, rasterizer{rasterizer_},
cpu_memory{cpu_memory_}, runtime{runtime_}, gpu{gpu_} {
streamer_mask = 0;
for (size_t i = 0; i < static_cast<size_t>(QueryType::MaxQueryTypes); i++) {
streamers[i] = runtime.GetStreamerInterface(static_cast<QueryType>(i));
if (streamers[i]) {
streamer_mask |= 1ULL << streamers[i]->GetId();
}
}
}
template <typename Func>
void ForEachStreamerIn(u64 mask, Func&& func) {
static constexpr bool RETURNS_BOOL =
std::is_same_v<std::invoke_result<Func, StreamerInterface*>, bool>;
while (mask != 0) {
size_t position = std::countr_zero(mask);
mask &= ~(1ULL << position);
if constexpr (RETURNS_BOOL) {
if (func(streamers[position])) {
return;
}
} else {
func(streamers[position]);
}
}
}
template <typename Func>
void ForEachStreamer(Func&& func) {
ForEachStreamerIn(streamer_mask, func);
}
QueryBase* ObtainQuery(QueryCacheBase<Traits>::QueryLocation location) {
size_t which_stream = location.stream_id.Value();
auto* streamer = streamers[which_stream];
if (!streamer) {
return nullptr;
}
return streamer->GetQuery(location.query_id.Value());
}
QueryCacheBase<Traits>* owner;
VideoCore::RasterizerInterface& rasterizer;
Core::Memory::Memory& cpu_memory;
RuntimeType& runtime;
Tegra::GPU& gpu;
std::array<StreamerInterface*, static_cast<size_t>(QueryType::MaxQueryTypes)> streamers;
u64 streamer_mask;
std::mutex flush_guard;
std::deque<u64> flushes_pending;
std::vector<QueryCacheBase<Traits>::QueryLocation> pending_unregister;
};
template <typename Traits>
QueryCacheBase<Traits>::QueryCacheBase(Tegra::GPU& gpu_,
VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, RuntimeType& runtime_)
: cached_queries{} {
impl = std::make_unique<QueryCacheBase<Traits>::QueryCacheBaseImpl>(
this, rasterizer_, cpu_memory_, runtime_, gpu_);
}
template <typename Traits>
QueryCacheBase<Traits>::~QueryCacheBase() = default;
template <typename Traits>
void QueryCacheBase<Traits>::CounterEnable(QueryType counter_type, bool is_enabled) {
size_t index = static_cast<size_t>(counter_type);
StreamerInterface* streamer = impl->streamers[index];
if (!streamer) [[unlikely]] {
UNREACHABLE();
return;
}
if (is_enabled) {
streamer->StartCounter();
} else {
streamer->PauseCounter();
}
}
template <typename Traits>
void QueryCacheBase<Traits>::CounterClose(QueryType counter_type) {
size_t index = static_cast<size_t>(counter_type);
StreamerInterface* streamer = impl->streamers[index];
if (!streamer) [[unlikely]] {
UNREACHABLE();
return;
}
streamer->CloseCounter();
}
template <typename Traits>
void QueryCacheBase<Traits>::CounterReset(QueryType counter_type) {
size_t index = static_cast<size_t>(counter_type);
StreamerInterface* streamer = impl->streamers[index];
if (!streamer) [[unlikely]] {
UNIMPLEMENTED();
return;
}
streamer->ResetCounter();
}
template <typename Traits>
void QueryCacheBase<Traits>::BindToChannel(s32 id) {
VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>::BindToChannel(id);
impl->runtime.Bind3DEngine(maxwell3d);
}
template <typename Traits>
void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type,
QueryPropertiesFlags flags, u32 payload, u32 subreport) {
const bool has_timestamp = True(flags & QueryPropertiesFlags::HasTimeout);
const bool is_fence = True(flags & QueryPropertiesFlags::IsAFence);
size_t streamer_id = static_cast<size_t>(counter_type);
auto* streamer = impl->streamers[streamer_id];
if (streamer == nullptr) [[unlikely]] {
counter_type = QueryType::Payload;
payload = 1U;
streamer_id = static_cast<size_t>(counter_type);
streamer = impl->streamers[streamer_id];
}
auto cpu_addr_opt = gpu_memory->GpuToCpuAddress(addr);
if (!cpu_addr_opt) [[unlikely]] {
return;
}
VAddr cpu_addr = *cpu_addr_opt;
const size_t new_query_id = streamer->WriteCounter(cpu_addr, has_timestamp, payload, subreport);
auto* query = streamer->GetQuery(new_query_id);
if (is_fence) {
query->flags |= QueryFlagBits::IsFence;
}
QueryLocation query_location{};
query_location.stream_id.Assign(static_cast<u32>(streamer_id));
query_location.query_id.Assign(static_cast<u32>(new_query_id));
const auto gen_caching_indexing = [](VAddr cur_addr) {
return std::make_pair<u64, u32>(cur_addr >> Core::Memory::YUZU_PAGEBITS,
static_cast<u32>(cur_addr & Core::Memory::YUZU_PAGEMASK));
};
u8* pointer = impl->cpu_memory.GetPointer(cpu_addr);
u8* pointer_timestamp = impl->cpu_memory.GetPointer(cpu_addr + 8);
bool is_synced = !Settings::IsGPULevelHigh() && is_fence;
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
pointer, pointer_timestamp] {
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
if (!is_synced) [[likely]] {
impl->pending_unregister.push_back(query_location);
}
return;
}
if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] {
UNREACHABLE();
return;
}
query_base->value += streamer->GetAmmendValue();
streamer->SetAccumulationValue(query_base->value);
if (True(query_base->flags & QueryFlagBits::HasTimestamp)) {
u64 timestamp = impl->gpu.GetTicks();
std::memcpy(pointer_timestamp, &timestamp, sizeof(timestamp));
std::memcpy(pointer, &query_base->value, sizeof(query_base->value));
} else {
u32 value = static_cast<u32>(query_base->value);
std::memcpy(pointer, &value, sizeof(value));
}
if (!is_synced) [[likely]] {
impl->pending_unregister.push_back(query_location);
}
});
if (is_fence) {
impl->rasterizer.SignalFence(std::move(operation));
} else {
if (!Settings::IsGPULevelHigh() && counter_type == QueryType::Payload) {
if (has_timestamp) {
u64 timestamp = impl->gpu.GetTicks();
u64 value = static_cast<u64>(payload);
std::memcpy(pointer_timestamp, &timestamp, sizeof(timestamp));
std::memcpy(pointer, &value, sizeof(value));
} else {
std::memcpy(pointer, &payload, sizeof(payload));
}
streamer->Free(new_query_id);
return;
}
impl->rasterizer.SyncOperation(std::move(operation));
}
if (is_synced) {
streamer->Free(new_query_id);
return;
}
auto [cont_addr, base] = gen_caching_indexing(cpu_addr);
{
std::scoped_lock lock(cache_mutex);
auto it1 = cached_queries.try_emplace(cont_addr);
auto& sub_container = it1.first->second;
auto it_current = sub_container.find(base);
if (it_current == sub_container.end()) {
sub_container.insert_or_assign(base, query_location);
return;
}
auto* old_query = impl->ObtainQuery(it_current->second);
old_query->flags |= QueryFlagBits::IsRewritten;
sub_container.insert_or_assign(base, query_location);
}
}
template <typename Traits>
void QueryCacheBase<Traits>::UnregisterPending() {
const auto gen_caching_indexing = [](VAddr cur_addr) {
return std::make_pair<u64, u32>(cur_addr >> Core::Memory::YUZU_PAGEBITS,
static_cast<u32>(cur_addr & Core::Memory::YUZU_PAGEMASK));
};
std::scoped_lock lock(cache_mutex);
for (QueryLocation loc : impl->pending_unregister) {
const auto [streamer_id, query_id] = loc.unpack();
auto* streamer = impl->streamers[streamer_id];
if (!streamer) [[unlikely]] {
continue;
}
auto* query = streamer->GetQuery(query_id);
auto [cont_addr, base] = gen_caching_indexing(query->guest_address);
auto it1 = cached_queries.find(cont_addr);
if (it1 != cached_queries.end()) {
auto it2 = it1->second.find(base);
if (it2 != it1->second.end()) {
if (it2->second.raw == loc.raw) {
it1->second.erase(it2);
}
}
}
streamer->Free(query_id);
}
impl->pending_unregister.clear();
}
template <typename Traits>
void QueryCacheBase<Traits>::NotifyWFI() {
bool should_sync = false;
impl->ForEachStreamer(
[&should_sync](StreamerInterface* streamer) { should_sync |= streamer->HasPendingSync(); });
if (!should_sync) {
return;
}
impl->ForEachStreamer([](StreamerInterface* streamer) { streamer->PresyncWrites(); });
impl->runtime.Barriers(true);
impl->ForEachStreamer([](StreamerInterface* streamer) { streamer->SyncWrites(); });
impl->runtime.Barriers(false);
}
template <typename Traits>
void QueryCacheBase<Traits>::NotifySegment(bool resume) {
if (resume) {
impl->runtime.ResumeHostConditionalRendering();
} else {
CounterClose(VideoCommon::QueryType::ZPassPixelCount64);
CounterClose(VideoCommon::QueryType::StreamingByteCount);
impl->runtime.PauseHostConditionalRendering();
}
}
template <typename Traits>
bool QueryCacheBase<Traits>::AccelerateHostConditionalRendering() {
bool qc_dirty = false;
const auto gen_lookup = [this, &qc_dirty](GPUVAddr address) -> VideoCommon::LookupData {
auto cpu_addr_opt = gpu_memory->GpuToCpuAddress(address);
if (!cpu_addr_opt) [[unlikely]] {
return VideoCommon::LookupData{
.address = 0,
.found_query = nullptr,
};
}
VAddr cpu_addr = *cpu_addr_opt;
std::scoped_lock lock(cache_mutex);
auto it1 = cached_queries.find(cpu_addr >> Core::Memory::YUZU_PAGEBITS);
if (it1 == cached_queries.end()) {
return VideoCommon::LookupData{
.address = cpu_addr,
.found_query = nullptr,
};
}
auto& sub_container = it1->second;
auto it_current = sub_container.find(cpu_addr & Core::Memory::YUZU_PAGEMASK);
if (it_current == sub_container.end()) {
auto it_current_2 = sub_container.find((cpu_addr & Core::Memory::YUZU_PAGEMASK) + 4);
if (it_current_2 == sub_container.end()) {
return VideoCommon::LookupData{
.address = cpu_addr,
.found_query = nullptr,
};
}
}
auto* query = impl->ObtainQuery(it_current->second);
qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) &&
False(query->flags & QueryFlagBits::IsGuestSynced);
return VideoCommon::LookupData{
.address = cpu_addr,
.found_query = query,
};
};
auto& regs = maxwell3d->regs;
if (regs.render_enable_override != Maxwell::Regs::RenderEnable::Override::UseRenderEnable) {
impl->runtime.EndHostConditionalRendering();
return false;
}
const ComparisonMode mode = static_cast<ComparisonMode>(regs.render_enable.mode);
const GPUVAddr address = regs.render_enable.Address();
switch (mode) {
case ComparisonMode::True:
impl->runtime.EndHostConditionalRendering();
return false;
case ComparisonMode::False:
impl->runtime.EndHostConditionalRendering();
return false;
case ComparisonMode::Conditional: {
VideoCommon::LookupData object_1{gen_lookup(address)};
return impl->runtime.HostConditionalRenderingCompareValue(object_1, qc_dirty);
}
case ComparisonMode::IfEqual: {
VideoCommon::LookupData object_1{gen_lookup(address)};
VideoCommon::LookupData object_2{gen_lookup(address + 16)};
return impl->runtime.HostConditionalRenderingCompareValues(object_1, object_2, qc_dirty,
true);
}
case ComparisonMode::IfNotEqual: {
VideoCommon::LookupData object_1{gen_lookup(address)};
VideoCommon::LookupData object_2{gen_lookup(address + 16)};
return impl->runtime.HostConditionalRenderingCompareValues(object_1, object_2, qc_dirty,
false);
}
default:
return false;
}
}
// Async downloads
template <typename Traits>
void QueryCacheBase<Traits>::CommitAsyncFlushes() {
// Make sure to have the results synced in Host.
NotifyWFI();
u64 mask{};
{
std::scoped_lock lk(impl->flush_guard);
impl->ForEachStreamer([&mask](StreamerInterface* streamer) {
bool local_result = streamer->HasUnsyncedQueries();
if (local_result) {
mask |= 1ULL << streamer->GetId();
}
});
impl->flushes_pending.push_back(mask);
}
std::function<void()> func([this] { UnregisterPending(); });
impl->rasterizer.SyncOperation(std::move(func));
if (mask == 0) {
return;
}
u64 ran_mask = ~mask;
while (mask) {
impl->ForEachStreamerIn(mask, [&mask, &ran_mask](StreamerInterface* streamer) {
u64 dep_mask = streamer->GetDependentMask();
if ((dep_mask & ~ran_mask) != 0) {
return;
}
u64 index = streamer->GetId();
ran_mask |= (1ULL << index);
mask &= ~(1ULL << index);
streamer->PushUnsyncedQueries();
});
}
}
template <typename Traits>
bool QueryCacheBase<Traits>::HasUncommittedFlushes() const {
bool result = false;
impl->ForEachStreamer([&result](StreamerInterface* streamer) {
result |= streamer->HasUnsyncedQueries();
return result;
});
return result;
}
template <typename Traits>
bool QueryCacheBase<Traits>::ShouldWaitAsyncFlushes() {
std::scoped_lock lk(impl->flush_guard);
return !impl->flushes_pending.empty() && impl->flushes_pending.front() != 0ULL;
}
template <typename Traits>
void QueryCacheBase<Traits>::PopAsyncFlushes() {
u64 mask;
{
std::scoped_lock lk(impl->flush_guard);
mask = impl->flushes_pending.front();
impl->flushes_pending.pop_front();
}
if (mask == 0) {
return;
}
u64 ran_mask = ~mask;
while (mask) {
impl->ForEachStreamerIn(mask, [&mask, &ran_mask](StreamerInterface* streamer) {
u64 dep_mask = streamer->GetDependenceMask();
if ((dep_mask & ~ran_mask) != 0) {
return;
}
u64 index = streamer->GetId();
ran_mask |= (1ULL << index);
mask &= ~(1ULL << index);
streamer->PopUnsyncedQueries();
});
}
}
// Invalidation
template <typename Traits>
void QueryCacheBase<Traits>::InvalidateQuery(QueryCacheBase<Traits>::QueryLocation location) {
auto* query_base = impl->ObtainQuery(location);
if (!query_base) {
return;
}
query_base->flags |= QueryFlagBits::IsInvalidated;
}
template <typename Traits>
bool QueryCacheBase<Traits>::IsQueryDirty(QueryCacheBase<Traits>::QueryLocation location) {
auto* query_base = impl->ObtainQuery(location);
if (!query_base) {
return false;
}
return True(query_base->flags & QueryFlagBits::IsHostManaged) &&
False(query_base->flags & QueryFlagBits::IsGuestSynced);
}
template <typename Traits>
bool QueryCacheBase<Traits>::SemiFlushQueryDirty(QueryCacheBase<Traits>::QueryLocation location) {
auto* query_base = impl->ObtainQuery(location);
if (!query_base) {
return false;
}
if (True(query_base->flags & QueryFlagBits::IsFinalValueSynced) &&
False(query_base->flags & QueryFlagBits::IsGuestSynced)) {
auto* ptr = impl->cpu_memory.GetPointer(query_base->guest_address);
if (True(query_base->flags & QueryFlagBits::HasTimestamp)) {
std::memcpy(ptr, &query_base->value, sizeof(query_base->value));
return false;
}
u32 value_l = static_cast<u32>(query_base->value);
std::memcpy(ptr, &value_l, sizeof(value_l));
return false;
}
return True(query_base->flags & QueryFlagBits::IsHostManaged) &&
False(query_base->flags & QueryFlagBits::IsGuestSynced);
}
template <typename Traits>
void QueryCacheBase<Traits>::RequestGuestHostSync() {
impl->rasterizer.ReleaseFences();
}
} // namespace VideoCommon

View File

@ -1,181 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <functional>
#include <mutex>
#include <optional>
#include <span>
#include <unordered_map>
#include <utility>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/memory.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/query_cache/query_base.h"
#include "video_core/query_cache/types.h"
namespace Core::Memory {
class Memory;
}
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra {
class GPU;
}
namespace VideoCommon {
struct LookupData {
VAddr address;
QueryBase* found_query;
};
template <typename Traits>
class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
using RuntimeType = typename Traits::RuntimeType;
public:
union QueryLocation {
BitField<27, 5, u32> stream_id;
BitField<0, 27, u32> query_id;
u32 raw;
std::pair<size_t, size_t> unpack() const {
return {static_cast<size_t>(stream_id.Value()), static_cast<size_t>(query_id.Value())};
}
};
explicit QueryCacheBase(Tegra::GPU& gpu, VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, RuntimeType& runtime_);
~QueryCacheBase();
void InvalidateRegion(VAddr addr, std::size_t size) {
IterateCache<true>(addr, size,
[this](QueryLocation location) { InvalidateQuery(location); });
}
void FlushRegion(VAddr addr, std::size_t size) {
bool result = false;
IterateCache<false>(addr, size, [this, &result](QueryLocation location) {
result |= SemiFlushQueryDirty(location);
return result;
});
if (result) {
RequestGuestHostSync();
}
}
static u64 BuildMask(std::span<const QueryType> types) {
u64 mask = 0;
for (auto query_type : types) {
mask |= 1ULL << (static_cast<u64>(query_type));
}
return mask;
}
/// Return true when a CPU region is modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size) {
bool result = false;
IterateCache<false>(addr, size, [this, &result](QueryLocation location) {
result |= IsQueryDirty(location);
return result;
});
return result;
}
void CounterEnable(QueryType counter_type, bool is_enabled);
void CounterReset(QueryType counter_type);
void CounterClose(QueryType counter_type);
void CounterReport(GPUVAddr addr, QueryType counter_type, QueryPropertiesFlags flags,
u32 payload, u32 subreport);
void NotifyWFI();
bool AccelerateHostConditionalRendering();
// Async downloads
void CommitAsyncFlushes();
bool HasUncommittedFlushes() const;
bool ShouldWaitAsyncFlushes();
void PopAsyncFlushes();
void NotifySegment(bool resume);
void BindToChannel(s32 id) override;
protected:
template <bool remove_from_cache, typename Func>
void IterateCache(VAddr addr, std::size_t size, Func&& func) {
static constexpr bool RETURNS_BOOL =
std::is_same_v<std::invoke_result<Func, QueryLocation>, bool>;
const u64 addr_begin = addr;
const u64 addr_end = addr_begin + size;
const u64 page_end = addr_end >> Core::Memory::YUZU_PAGEBITS;
std::scoped_lock lock(cache_mutex);
for (u64 page = addr_begin >> Core::Memory::YUZU_PAGEBITS; page <= page_end; ++page) {
const u64 page_start = page << Core::Memory::YUZU_PAGEBITS;
const auto in_range = [page_start, addr_begin, addr_end](const u32 query_location) {
const u64 cache_begin = page_start + query_location;
const u64 cache_end = cache_begin + sizeof(u32);
return cache_begin < addr_end && addr_begin < cache_end;
};
const auto& it = cached_queries.find(page);
if (it == std::end(cached_queries)) {
continue;
}
auto& contents = it->second;
for (auto& query : contents) {
if (!in_range(query.first)) {
continue;
}
if constexpr (RETURNS_BOOL) {
if (func(query.second)) {
return;
}
} else {
func(query.second);
}
}
if constexpr (remove_from_cache) {
const auto in_range2 = [&](const std::pair<u32, QueryLocation>& pair) {
return in_range(pair.first);
};
std::erase_if(contents, in_range2);
}
}
}
using ContentCache = std::unordered_map<u64, std::unordered_map<u32, QueryLocation>>;
void InvalidateQuery(QueryLocation location);
bool IsQueryDirty(QueryLocation location);
bool SemiFlushQueryDirty(QueryLocation location);
void RequestGuestHostSync();
void UnregisterPending();
std::unordered_map<u64, std::unordered_map<u32, QueryLocation>> cached_queries;
std::mutex cache_mutex;
struct QueryCacheBaseImpl;
friend struct QueryCacheBaseImpl;
friend RuntimeType;
std::unique_ptr<QueryCacheBaseImpl> impl;
};
} // namespace VideoCommon

View File

@ -1,149 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <deque>
#include <optional>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/query_cache/bank_base.h"
#include "video_core/query_cache/query_base.h"
namespace VideoCommon {
class StreamerInterface {
public:
explicit StreamerInterface(size_t id_) : id{id_}, dependence_mask{}, dependent_mask{} {}
virtual ~StreamerInterface() = default;
virtual QueryBase* GetQuery(size_t id) = 0;
virtual void StartCounter() {
/* Do Nothing */
}
virtual void PauseCounter() {
/* Do Nothing */
}
virtual void ResetCounter() {
/* Do Nothing */
}
virtual void CloseCounter() {
/* Do Nothing */
}
virtual bool HasPendingSync() const {
return false;
}
virtual void PresyncWrites() {
/* Do Nothing */
}
virtual void SyncWrites() {
/* Do Nothing */
}
virtual size_t WriteCounter(VAddr address, bool has_timestamp, u32 value,
std::optional<u32> subreport = std::nullopt) = 0;
virtual bool HasUnsyncedQueries() const {
return false;
}
virtual void PushUnsyncedQueries() {
/* Do Nothing */
}
virtual void PopUnsyncedQueries() {
/* Do Nothing */
}
virtual void Free(size_t query_id) = 0;
size_t GetId() const {
return id;
}
u64 GetDependenceMask() const {
return dependence_mask;
}
u64 GetDependentMask() const {
return dependence_mask;
}
u64 GetAmmendValue() const {
return ammend_value;
}
void SetAccumulationValue(u64 new_value) {
acumulation_value = new_value;
}
protected:
void MakeDependent(StreamerInterface* depend_on) {
dependence_mask |= 1ULL << depend_on->id;
depend_on->dependent_mask |= 1ULL << id;
}
const size_t id;
u64 dependence_mask;
u64 dependent_mask;
u64 ammend_value{};
u64 acumulation_value{};
};
template <typename QueryType>
class SimpleStreamer : public StreamerInterface {
public:
explicit SimpleStreamer(size_t id_) : StreamerInterface{id_} {}
virtual ~SimpleStreamer() = default;
protected:
virtual QueryType* GetQuery(size_t query_id) override {
if (query_id < slot_queries.size()) {
return &slot_queries[query_id];
}
return nullptr;
}
virtual void Free(size_t query_id) override {
std::scoped_lock lk(guard);
ReleaseQuery(query_id);
}
template <typename... Args, typename = decltype(QueryType(std::declval<Args>()...))>
size_t BuildQuery(Args&&... args) {
std::scoped_lock lk(guard);
if (!old_queries.empty()) {
size_t new_id = old_queries.front();
old_queries.pop_front();
new (&slot_queries[new_id]) QueryType(std::forward<Args>(args)...);
return new_id;
}
size_t new_id = slot_queries.size();
slot_queries.emplace_back(std::forward<Args>(args)...);
return new_id;
}
void ReleaseQuery(size_t query_id) {
if (query_id < slot_queries.size()) {
old_queries.push_back(query_id);
return;
}
UNREACHABLE();
}
std::mutex guard;
std::deque<QueryType> slot_queries;
std::deque<size_t> old_queries;
};
} // namespace VideoCommon

View File

@ -1,74 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace VideoCommon {
enum class QueryPropertiesFlags : u32 {
HasTimeout = 1 << 0,
IsAFence = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(QueryPropertiesFlags)
// This should always be equivalent to maxwell3d Report Semaphore Reports
enum class QueryType : u32 {
Payload = 0, // "None" in docs, but confirmed via hardware to return the payload
VerticesGenerated = 1,
ZPassPixelCount = 2,
PrimitivesGenerated = 3,
AlphaBetaClocks = 4,
VertexShaderInvocations = 5,
StreamingPrimitivesNeededMinusSucceeded = 6,
GeometryShaderInvocations = 7,
GeometryShaderPrimitivesGenerated = 9,
ZCullStats0 = 10,
StreamingPrimitivesSucceeded = 11,
ZCullStats1 = 12,
StreamingPrimitivesNeeded = 13,
ZCullStats2 = 14,
ClipperInvocations = 15,
ZCullStats3 = 16,
ClipperPrimitivesGenerated = 17,
VtgPrimitivesOut = 18,
PixelShaderInvocations = 19,
ZPassPixelCount64 = 21,
IEEECleanColorTarget = 24,
IEEECleanZetaTarget = 25,
StreamingByteCount = 26,
TessellationInitInvocations = 27,
BoundingRectangle = 28,
TessellationShaderInvocations = 29,
TotalStreamingPrimitivesNeededMinusSucceeded = 30,
TessellationShaderPrimitivesGenerated = 31,
// max.
MaxQueryTypes,
};
// Comparison modes for Host Conditional Rendering
enum class ComparisonMode : u32 {
False = 0,
True = 1,
Conditional = 2,
IfEqual = 3,
IfNotEqual = 4,
MaxComparisonMode,
};
// Reduction ops.
enum class ReductionOp : u32 {
RedAdd = 0,
RedMin = 1,
RedMax = 2,
RedInc = 3,
RedDec = 4,
RedAnd = 5,
RedOr = 6,
RedXor = 7,
MaxReductionOp,
};
} // namespace VideoCommon

View File

@ -12,7 +12,6 @@
#include "video_core/cache_types.h" #include "video_core/cache_types.h"
#include "video_core/engines/fermi_2d.h" #include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/query_cache/types.h"
#include "video_core/rasterizer_download_area.h" #include "video_core/rasterizer_download_area.h"
namespace Tegra { namespace Tegra {
@ -27,6 +26,11 @@ struct ChannelState;
namespace VideoCore { namespace VideoCore {
enum class QueryType {
SamplesPassed,
};
constexpr std::size_t NumQueryTypes = 1;
enum class LoadCallbackStage { enum class LoadCallbackStage {
Prepare, Prepare,
Build, Build,
@ -54,11 +58,10 @@ public:
virtual void DispatchCompute() = 0; virtual void DispatchCompute() = 0;
/// Resets the counter of a query /// Resets the counter of a query
virtual void ResetCounter(VideoCommon::QueryType type) = 0; virtual void ResetCounter(QueryType type) = 0;
/// Records a GPU query and caches it /// Records a GPU query and caches it
virtual void Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0;
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) = 0;
/// Signal an uniform buffer binding /// Signal an uniform buffer binding
virtual void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, virtual void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
@ -80,7 +83,7 @@ public:
virtual void SignalReference() = 0; virtual void SignalReference() = 0;
/// Release all pending fences. /// Release all pending fences.
virtual void ReleaseFences(bool force = true) = 0; virtual void ReleaseFences() = 0;
/// Notify rasterizer that all caches should be flushed to Switch memory /// Notify rasterizer that all caches should be flushed to Switch memory
virtual void FlushAll() = 0; virtual void FlushAll() = 0;

View File

@ -26,18 +26,16 @@ void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
void RasterizerNull::DrawTexture() {} void RasterizerNull::DrawTexture() {}
void RasterizerNull::Clear(u32 layer_count) {} void RasterizerNull::Clear(u32 layer_count) {}
void RasterizerNull::DispatchCompute() {} void RasterizerNull::DispatchCompute() {}
void RasterizerNull::ResetCounter(VideoCommon::QueryType type) {} void RasterizerNull::ResetCounter(VideoCore::QueryType type) {}
void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) { std::optional<u64> timestamp) {
if (!gpu_memory) { if (!gpu_memory) {
return; return;
} }
if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) {
u64 ticks = m_gpu.GetTicks(); gpu_memory->Write(gpu_addr, u64{0});
gpu_memory->Write<u64>(gpu_addr + 8, ticks); if (timestamp) {
gpu_memory->Write<u64>(gpu_addr, static_cast<u64>(payload)); gpu_memory->Write(gpu_addr + 8, *timestamp);
} else {
gpu_memory->Write<u32>(gpu_addr, payload);
} }
} }
void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
@ -76,7 +74,7 @@ void RasterizerNull::SignalSyncPoint(u32 value) {
syncpoint_manager.IncrementHost(value); syncpoint_manager.IncrementHost(value);
} }
void RasterizerNull::SignalReference() {} void RasterizerNull::SignalReference() {}
void RasterizerNull::ReleaseFences(bool) {} void RasterizerNull::ReleaseFences() {}
void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {} void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size, VideoCommon::CacheType) {}
void RasterizerNull::WaitForIdle() {} void RasterizerNull::WaitForIdle() {}
void RasterizerNull::FragmentBarrier() {} void RasterizerNull::FragmentBarrier() {}

View File

@ -42,9 +42,8 @@ public:
void DrawTexture() override; void DrawTexture() override;
void Clear(u32 layer_count) override; void Clear(u32 layer_count) override;
void DispatchCompute() override; void DispatchCompute() override;
void ResetCounter(VideoCommon::QueryType type) override; void ResetCounter(VideoCore::QueryType type) override;
void Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) override;
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
void FlushAll() override; void FlushAll() override;
@ -64,7 +63,7 @@ public:
void SyncOperation(std::function<void()>&& func) override; void SyncOperation(std::function<void()>&& func) override;
void SignalSyncPoint(u32 value) override; void SignalSyncPoint(u32 value) override;
void SignalReference() override; void SignalReference() override;
void ReleaseFences(bool force) override; void ReleaseFences() override;
void FlushAndInvalidateRegion( void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void WaitForIdle() override; void WaitForIdle() override;

View File

@ -27,7 +27,7 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
} // Anonymous namespace } // Anonymous namespace
QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_) QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
: QueryCacheLegacy(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {} : QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {}
QueryCache::~QueryCache() = default; QueryCache::~QueryCache() = default;

View File

@ -26,7 +26,7 @@ class RasterizerOpenGL;
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>; using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
class QueryCache final class QueryCache final
: public VideoCommon::QueryCacheLegacy<QueryCache, CachedQuery, CounterStream, HostCounter> { : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
public: public:
explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_); explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_);
~QueryCache(); ~QueryCache();

View File

@ -396,39 +396,13 @@ void RasterizerOpenGL::DispatchCompute() {
has_written_global_memory |= pipeline->WritesGlobalMemory(); has_written_global_memory |= pipeline->WritesGlobalMemory();
} }
void RasterizerOpenGL::ResetCounter(VideoCommon::QueryType type) { void RasterizerOpenGL::ResetCounter(VideoCore::QueryType type) {
if (type == VideoCommon::QueryType::ZPassPixelCount64) { query_cache.ResetCounter(type);
query_cache.ResetCounter(VideoCore::QueryType::SamplesPassed);
}
} }
void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCore::QueryType type,
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) { std::optional<u64> timestamp) {
if (type == VideoCommon::QueryType::ZPassPixelCount64) { query_cache.Query(gpu_addr, type, timestamp);
if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) {
query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, {gpu.GetTicks()});
} else {
query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, std::nullopt);
}
return;
}
if (type != VideoCommon::QueryType::Payload) {
payload = 1u;
}
std::function<void()> func([this, gpu_addr, flags, memory_manager = gpu_memory, payload]() {
if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) {
u64 ticks = gpu.GetTicks();
memory_manager->Write<u64>(gpu_addr + 8, ticks);
memory_manager->Write<u64>(gpu_addr, static_cast<u64>(payload));
} else {
memory_manager->Write<u32>(gpu_addr, payload);
}
});
if (True(flags & VideoCommon::QueryPropertiesFlags::IsAFence)) {
SignalFence(std::move(func));
return;
}
func();
} }
void RasterizerOpenGL::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, void RasterizerOpenGL::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
@ -599,8 +573,8 @@ void RasterizerOpenGL::SignalReference() {
fence_manager.SignalOrdering(); fence_manager.SignalOrdering();
} }
void RasterizerOpenGL::ReleaseFences(bool force) { void RasterizerOpenGL::ReleaseFences() {
fence_manager.WaitPendingFences(force); fence_manager.WaitPendingFences();
} }
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size, void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size,

View File

@ -86,9 +86,8 @@ public:
void DrawTexture() override; void DrawTexture() override;
void Clear(u32 layer_count) override; void Clear(u32 layer_count) override;
void DispatchCompute() override; void DispatchCompute() override;
void ResetCounter(VideoCommon::QueryType type) override; void ResetCounter(VideoCore::QueryType type) override;
void Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) override;
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override;
void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; void DisableGraphicsUniformBuffer(size_t stage, u32 index) override;
void FlushAll() override; void FlushAll() override;
@ -108,7 +107,7 @@ public:
void SyncOperation(std::function<void()>&& func) override; void SyncOperation(std::function<void()>&& func) override;
void SignalSyncPoint(u32 value) override; void SignalSyncPoint(u32 value) override;
void SignalReference() override; void SignalReference() override;
void ReleaseFences(bool force = true) override; void ReleaseFences() override;
void FlushAndInvalidateRegion( void FlushAndInvalidateRegion(
VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; VAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override;
void WaitForIdle() override; void WaitForIdle() override;

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