Compare commits

...

6 Commits

Author SHA1 Message Date
e9d7bd2534 Android 190 2024-01-12 01:00:45 +00:00
57c6d95eb5 Merge yuzu-emu#12642 2024-01-12 01:00:45 +00:00
b83c362cc9 Merge yuzu-emu#12612 2024-01-12 01:00:45 +00:00
0d8ce6cc50 Merge yuzu-emu#12610 2024-01-12 01:00:45 +00:00
67b104dc5a Merge yuzu-emu#12605 2024-01-12 01:00:45 +00:00
8f979a3153 Merge yuzu-emu#12579 2024-01-12 01:00:45 +00:00
212 changed files with 7121 additions and 2289 deletions

View File

@ -1,3 +1,16 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12579](https://github.com/yuzu-emu/yuzu-android//pull/12579) | [`66ae60a9e`](https://github.com/yuzu-emu/yuzu-android//pull/12579/files) | Core: Implement Device Mapping & GPU SMMU | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12605](https://github.com/yuzu-emu/yuzu-android//pull/12605) | [`0683fb5a7`](https://github.com/yuzu-emu/yuzu-android//pull/12605/files) | service: hid: Create abstracted pad structure | [german77](https://github.com/german77/) | Yes |
| [12610](https://github.com/yuzu-emu/yuzu-android//pull/12610) | [`200b371d1`](https://github.com/yuzu-emu/yuzu-android//pull/12610/files) | server_manager: respond to session close correctly | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12612](https://github.com/yuzu-emu/yuzu-android//pull/12612) | [`aae9eea53`](https://github.com/yuzu-emu/yuzu-android//pull/12612/files) | fsp-srv: use program registry for SetCurrentProcess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12642](https://github.com/yuzu-emu/yuzu-android//pull/12642) | [`d3ba6b334`](https://github.com/yuzu-emu/yuzu-android//pull/12642/files) | android: Refactor list adapters | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import androidx.recyclerview.widget.RecyclerView
/**
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
* code used in every [RecyclerView].
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
*/
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem === newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem == newItem
}
}
}

View File

@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of basic lists
* @param currentList The list to show initially
*/
abstract class AbstractListAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
open var currentList: List<Model>
) : RecyclerView.Adapter<Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter
* is passed in for position, [item] is added to the end of the list. Invokes [callback] last.
* @param item The item to add to the list
* @param position Index where [item] will be added
* @param callback Lambda that's called at the end of the list changes and has the added list
* position passed in as a parameter
*/
open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
val positionToUpdate: Int
if (position == -1) {
newList.add(item)
currentList = newList
positionToUpdate = currentList.size - 1
} else {
newList.add(position, item)
currentList = newList
positionToUpdate = position
}
onItemAdded(positionToUpdate, callback)
}
protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemInserted(position)
callback?.invoke(position)
}
/**
* Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param item New list item
* @param position Index where [item] will replace the existing list item
* @param callback Lambda that's called at the end of the list changes and has the changed list
* position passed in as a parameter
*/
fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList[position] = item
currentList = newList
onItemChanged(position, callback)
}
protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemChanged(position)
callback?.invoke(position)
}
/**
* Removes the list item at [position] in [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param position Index where the list item will be removed
* @param callback Lambda that's called at the end of the list changes and has the removed list
* position passed in as a parameter
*/
fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList.removeAt(position)
currentList = newList
onItemRemoved(position, callback)
}
protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemRemoved(position)
callback?.invoke(position)
}
/**
* Replaces [currentList] with [newList] and notifies the underlying adapter of the change.
* @param newList The new list to replace [currentList]
*/
@SuppressLint("NotifyDataSetChanged")
open fun replaceList(newList: List<Model>) {
currentList = newList
notifyDataSetChanged()
}
}

View File

@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import org.yuzu.yuzu_emu.model.SelectableItem
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of single selection UI updates
* @param currentList The list to show initially
* @param defaultSelection The default selection to use if no list items are selected by
* [SelectableItem.selected] or if the currently selected item is removed from the list
*/
abstract class AbstractSingleSelectionList<
Model : SelectableItem,
Holder : AbstractViewHolder<Model>
>(
final override var currentList: List<Model>,
private val defaultSelection: DefaultSelection = DefaultSelection.Start
) : AbstractListAdapter<Model, Holder>(currentList) {
var selectedItem = getDefaultSelection()
init {
findSelectedItem()
}
/**
* Changes the selection state of the [SelectableItem] that was selected and the previously selected
* item and notifies the underlying adapter of the change for those items. Invokes [callback] last.
* Does nothing if [position] is the same as the currently selected item.
* @param position Index of the item that was selected
* @param callback Lambda that's called at the end of the list changes and has the selected list
* position passed in as a parameter
*/
fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
if (position == selectedItem) {
return
}
val previouslySelectedItem = selectedItem
selectedItem = position
if (currentList.indices.contains(selectedItem)) {
currentList[selectedItem].onSelectionStateChanged(true)
}
if (currentList.indices.contains(previouslySelectedItem)) {
currentList[previouslySelectedItem].onSelectionStateChanged(false)
}
onItemChanged(previouslySelectedItem)
onItemChanged(selectedItem)
callback?.invoke(position)
}
/**
* Removes a given item from the list and notifies the underlying adapter of the change. If the
* currently selected item was the item that was removed, the item at the position provided
* by [defaultSelection] will be made the new selection. Invokes [callback] last.
* @param position Index of the item that was removed
* @param callback Lambda that's called at the end of the list changes and has the removed and
* selected list positions passed in as parameters
*/
fun removeSelectableItem(
position: Int,
callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)?
) {
removeItem(position)
if (position == selectedItem) {
selectedItem = getDefaultSelection()
currentList[selectedItem].onSelectionStateChanged(true)
onItemChanged(selectedItem)
} else if (position < selectedItem) {
selectedItem--
}
callback?.invoke(position, selectedItem)
}
override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) {
super.addItem(item, position, callback)
if (position <= selectedItem && position != -1) {
selectedItem++
}
}
override fun replaceList(newList: List<Model>) {
super.replaceList(newList)
findSelectedItem()
}
private fun findSelectedItem() {
for (i in currentList.indices) {
if (currentList[i].selected) {
selectedItem = i
break
}
}
}
private fun getDefaultSelection(): Int =
when (defaultSelection) {
DefaultSelection.Start -> currentList.indices.first
DefaultSelection.End -> currentList.indices.last
}
enum class DefaultSelection { Start, End }
}

View File

@ -5,48 +5,28 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Addon import org.yuzu.yuzu_emu.model.Addon
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>( class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return AddonViewHolder(it) } .also { return AddonViewHolder(it) }
} }
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
holder.bind(currentList[position])
inner class AddonViewHolder(val binding: ListItemAddonBinding) : inner class AddonViewHolder(val binding: ListItemAddonBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<Addon>(binding) {
fun bind(addon: Addon) { override fun bind(model: Addon) {
binding.root.setOnClickListener { binding.root.setOnClickListener {
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
} }
binding.title.text = addon.title binding.title.text = model.title
binding.version.text = addon.version binding.version.text = model.version
binding.addonSwitch.setOnCheckedChangeListener { _, checked -> binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
addon.enabled = checked model.enabled = checked
} }
binding.addonSwitch.isChecked = addon.enabled binding.addonSwitch.isChecked = model.enabled
}
}
private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
} }
} }
} }

View File

@ -4,13 +4,11 @@
package org.yuzu.yuzu_emu.adapters package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
@ -19,72 +17,58 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) : class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(), AbstractListAdapter<Applet, AppletAdapter.AppletViewHolder>(applets) {
View.OnClickListener {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): AppletAdapter.AppletViewHolder { ): AppletAdapter.AppletViewHolder {
CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false) CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@AppletAdapter) }
.also { return AppletViewHolder(it) } .also { return AppletViewHolder(it) }
} }
override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
holder.bind(applets[position])
override fun getItemCount(): Int = applets.size
override fun onClick(view: View) {
val applet = (view.tag as AppletViewHolder).applet
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
view.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
view.findNavController().navigate(action)
}
inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) : inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<Applet>(binding) {
lateinit var applet: Applet override fun bind(model: Applet) {
binding.title.setText(model.titleId)
init { binding.description.setText(model.descriptionId)
itemView.tag = this
}
fun bind(applet: Applet) {
this.applet = applet
binding.title.setText(applet.titleId)
binding.description.setText(applet.descriptionId)
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
binding.icon.context.resources, binding.icon.context.resources,
applet.iconId, model.iconId,
binding.icon.context.theme binding.icon.context.theme
) )
) )
binding.root.setOnClickListener { onClick(model) }
}
fun onClick(applet: Applet) {
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
binding.root.context,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
binding.root.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
binding.root.findNavController().navigate(action)
} }
} }
} }

View File

@ -4,12 +4,10 @@
package org.yuzu.yuzu_emu.adapters package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
@ -19,54 +17,43 @@ import org.yuzu.yuzu_emu.model.CabinetMode
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class CabinetLauncherDialogAdapter(val fragment: Fragment) : class CabinetLauncherDialogAdapter(val fragment: Fragment) :
RecyclerView.Adapter<CabinetModeViewHolder>(), AbstractListAdapter<CabinetMode, CabinetModeViewHolder>(
View.OnClickListener { CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList()
private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
.also { return CabinetModeViewHolder(it) } .also { return CabinetModeViewHolder(it) }
} }
override fun getItemCount(): Int = cabinetModes.size
override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
holder.bind(cabinetModes[position])
override fun onClick(view: View) {
val mode = (view.tag as CabinetModeViewHolder).cabinetMode
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
}
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<CabinetMode>(binding) {
lateinit var cabinetMode: CabinetMode override fun bind(model: CabinetMode) {
init {
itemView.tag = this
}
fun bind(cabinetMode: CabinetMode) {
this.cabinetMode = cabinetMode
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
binding.icon.context.resources, binding.icon.context.resources,
cabinetMode.iconId, model.iconId,
binding.icon.context.theme binding.icon.context.theme
) )
) )
binding.title.setText(cabinetMode.titleId) binding.title.setText(model.titleId)
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(mode: CabinetMode) {
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
} }
} }
} }

View File

@ -7,65 +7,39 @@ import android.text.TextUtils
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 androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) : class DriverAdapter(private val driverViewModel: DriverViewModel) :
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>( AbstractSingleSelectionList<Driver, DriverAdapter.DriverViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build() driverViewModel.driverList.value
) { ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
val binding = CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return DriverViewHolder(it) }
return DriverViewHolder(binding)
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
holder.bind(currentList[position])
private fun onSelectDriver(position: Int) {
driverViewModel.setSelectedDriverIndex(position)
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
}
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
if (driverViewModel.selectedDriver > position) {
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
}
if (GpuDriverHelper.customDriverSettingData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
} }
inner class DriverViewHolder(val binding: CardDriverOptionBinding) : inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<Driver>(binding) {
private lateinit var driverData: Pair<String, GpuDriverMetadata> override fun bind(model: Driver) {
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
this.driverData = driverData
val driver = driverData.second
binding.apply { binding.apply {
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition radioButton.isChecked = model.selected
root.setOnClickListener { root.setOnClickListener {
onSelectDriver(bindingAdapterPosition) selectItem(bindingAdapterPosition) {
driverViewModel.onDriverSelected(it)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
} }
buttonDelete.setOnClickListener { buttonDelete.setOnClickListener {
onDeleteDriver(driverData, bindingAdapterPosition) removeSelectableItem(
bindingAdapterPosition
) { removedPosition: Int, selectedPosition: Int ->
driverViewModel.onDriverRemoved(removedPosition, selectedPosition)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
} }
// Delay marquee by 3s // Delay marquee by 3s
@ -80,38 +54,19 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
}, },
3000 3000
) )
if (driver.name == null) { title.text = model.title
title.setText(R.string.system_gpu_driver) version.text = model.version
description.text = "" description.text = model.description
version.text = "" if (model.description.isNotEmpty()) {
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
} else {
title.text = driver.name
version.text = driver.version
description.text = driver.description
version.visibility = View.VISIBLE version.visibility = View.VISIBLE
description.visibility = View.VISIBLE description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE buttonDelete.visibility = View.VISIBLE
} else {
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
} }
} }
} }
} }
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
override fun areItemsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.first == newItem.first
}
override fun areContentsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.second == newItem.second
}
}
} }

View File

@ -8,19 +8,14 @@ import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardFolderBinding import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
ListAdapter<GameDir, FolderAdapter.FolderViewHolder>( AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() {
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
.also { return FolderViewHolder(it) } .also { return FolderViewHolder(it) }
} }
override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
holder.bind(currentList[position])
inner class FolderViewHolder(val binding: CardFolderBinding) : inner class FolderViewHolder(val binding: CardFolderBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<GameDir>(binding) {
private lateinit var gameDir: GameDir override fun bind(model: GameDir) {
fun bind(gameDir: GameDir) {
this.gameDir = gameDir
binding.apply { binding.apply {
path.text = Uri.parse(gameDir.uriString).path path.text = Uri.parse(model.uriString).path
path.postDelayed( path.postDelayed(
{ {
path.isSelected = true path.isSelected = true
@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
) )
buttonEdit.setOnClickListener { buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir) GameFolderPropertiesDialogFragment.newInstance(model)
.show( .show(
activity.supportFragmentManager, activity.supportFragmentManager,
GameFolderPropertiesDialogFragment.TAG GameFolderPropertiesDialogFragment.TAG
@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
} }
buttonDelete.setOnClickListener { buttonDelete.setOnClickListener {
gamesViewModel.removeFolder(this@FolderViewHolder.gameDir) gamesViewModel.removeFolder(model)
} }
} }
} }
} }
private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
}
} }

View File

@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable
import android.net.Uri import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
@ -25,10 +24,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) : class GameAdapter(private val activity: AppCompatActivity) :
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
View.OnClickListener,
View.OnLongClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
// Create a new view. CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return GameViewHolder(it) }
binding.cardGame.setOnClickListener(this)
binding.cardGame.setOnLongClickListener(this)
// Use that view to create a ViewHolder.
return GameViewHolder(binding)
}
override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Launches the game that was clicked on.
*
* @param view The card representing the game the user wants to play.
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(holder.game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
holder.game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(holder.game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, holder.game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
view.findNavController().navigate(action)
}
override fun onLongClick(view: View): Boolean {
val holder = view.tag as GameViewHolder
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
view.findNavController().navigate(action)
return true
} }
inner class GameViewHolder(val binding: CardGameBinding) : inner class GameViewHolder(val binding: CardGameBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<Game>(binding) {
lateinit var game: Game override fun bind(model: Game) {
init {
binding.cardGame.tag = this
}
fun bind(game: Game) {
this.game = game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
GameIconUtils.loadGameIcon(game, binding.imageGameScreen) GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.postDelayed( binding.textGameTitle.postDelayed(
{ {
@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) :
}, },
3000 3000
) )
}
}
private class DiffCallback : DiffUtil.ItemCallback<Game>() { binding.cardGame.setOnClickListener { onClick(model) }
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { binding.cardGame.setOnLongClickListener { onLongClick(model) }
return oldItem == newItem
} }
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { fun onClick(game: Game) {
return oldItem == newItem val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent =
Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
.setShortLabel(game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
binding.root.findNavController().navigate(action)
}
fun onLongClick(game: Game): Boolean {
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
binding.root.findNavController().navigate(action)
return true
} }
} }
} }

View File

@ -12,23 +12,22 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty import org.yuzu.yuzu_emu.model.SubmenuProperty
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter( class GamePropertiesAdapter(
private val viewLifecycle: LifecycleOwner, private val viewLifecycle: LifecycleOwner,
private var properties: List<GameProperty> private var properties: List<GameProperty>
) : ) : AbstractListAdapter<GameProperty, AbstractViewHolder<GameProperty>>(properties) {
RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): GamePropertyViewHolder { ): AbstractViewHolder<GameProperty> {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (viewType) { return when (viewType) {
PropertyType.Submenu.ordinal -> { PropertyType.Submenu.ordinal -> {
@ -51,11 +50,6 @@ class GamePropertiesAdapter(
} }
} }
override fun getItemCount(): Int = properties.size
override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
holder.bind(properties[position])
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when (properties[position]) { return when (properties[position]) {
is SubmenuProperty -> PropertyType.Submenu.ordinal is SubmenuProperty -> PropertyType.Submenu.ordinal
@ -63,14 +57,10 @@ class GamePropertiesAdapter(
} }
} }
sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(property: GameProperty)
}
inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) : inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
GamePropertyViewHolder(binding.root) { AbstractViewHolder<GameProperty>(binding) {
override fun bind(property: GameProperty) { override fun bind(model: GameProperty) {
val submenuProperty = property as SubmenuProperty val submenuProperty = model as SubmenuProperty
binding.root.setOnClickListener { binding.root.setOnClickListener {
submenuProperty.action.invoke() submenuProperty.action.invoke()
@ -108,9 +98,9 @@ class GamePropertiesAdapter(
} }
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) : inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
GamePropertyViewHolder(binding.root) { AbstractViewHolder<GameProperty>(binding) {
override fun bind(property: GameProperty) { override fun bind(model: GameProperty) {
val installableProperty = property as InstallableProperty val installableProperty = model as InstallableProperty
binding.title.setText(installableProperty.titleId) binding.title.setText(installableProperty.titleId)
binding.description.setText(installableProperty.descriptionId) binding.description.setText(installableProperty.descriptionId)

View File

@ -14,69 +14,37 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter( class HomeSettingAdapter(
private val activity: AppCompatActivity, private val activity: AppCompatActivity,
private val viewLifecycle: LifecycleOwner, private val viewLifecycle: LifecycleOwner,
var options: List<HomeSetting> options: List<HomeSetting>
) : ) : AbstractListAdapter<HomeSetting, HomeSettingAdapter.HomeOptionViewHolder>(options) {
RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
val binding = CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return HomeOptionViewHolder(it) }
binding.root.setOnClickListener(this)
return HomeOptionViewHolder(binding)
}
override fun getItemCount(): Int {
return options.size
}
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
holder.bind(options[position])
}
override fun onClick(view: View) {
val holder = view.tag as HomeOptionViewHolder
if (holder.option.isEnabled.invoke()) {
holder.option.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = holder.option.disabledTitleId,
descriptionId = holder.option.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
} }
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<HomeSetting>(binding) {
lateinit var option: HomeSetting override fun bind(model: HomeSetting) {
binding.optionTitle.text = activity.resources.getString(model.titleId)
init { binding.optionDescription.text = activity.resources.getString(model.descriptionId)
itemView.tag = this
}
fun bind(option: HomeSetting) {
this.option = option
binding.optionTitle.text = activity.resources.getString(option.titleId)
binding.optionDescription.text = activity.resources.getString(option.descriptionId)
binding.optionIcon.setImageDrawable( binding.optionIcon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
option.iconId, model.iconId,
activity.theme activity.theme
) )
) )
when (option.titleId) { when (model.titleId) {
R.string.get_early_access -> R.string.get_early_access ->
binding.optionLayout.background = binding.optionLayout.background =
ContextCompat.getDrawable( ContextCompat.getDrawable(
@ -85,7 +53,7 @@ class HomeSettingAdapter(
) )
} }
if (!option.isEnabled.invoke()) { if (!model.isEnabled.invoke()) {
binding.optionTitle.alpha = 0.5f binding.optionTitle.alpha = 0.5f
binding.optionDescription.alpha = 0.5f binding.optionDescription.alpha = 0.5f
binding.optionIcon.alpha = 0.5f binding.optionIcon.alpha = 0.5f
@ -93,7 +61,7 @@ class HomeSettingAdapter(
viewLifecycle.lifecycleScope.launch { viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
option.details.collect { updateOptionDetails(it) } model.details.collect { updateOptionDetails(it) }
} }
} }
binding.optionDetail.postDelayed( binding.optionDetail.postDelayed(
@ -103,6 +71,20 @@ class HomeSettingAdapter(
}, },
3000 3000
) )
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(model: HomeSetting) {
if (model.isEnabled.invoke()) {
model.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = model.disabledTitleId,
descriptionId = model.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
} }
private fun updateOptionDetails(detailString: String) { private fun updateOptionDetails(detailString: String) {

View File

@ -6,43 +6,33 @@ package org.yuzu.yuzu_emu.adapters
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 androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class InstallableAdapter(private val installables: List<Installable>) : class InstallableAdapter(installables: List<Installable>) :
RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() { AbstractListAdapter<Installable, InstallableAdapter.InstallableViewHolder>(installables) {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): InstallableAdapter.InstallableViewHolder { ): InstallableAdapter.InstallableViewHolder {
val binding = CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return InstallableViewHolder(it) }
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) : inner class InstallableViewHolder(val binding: CardInstallableBinding) :
RecyclerView.ViewHolder(binding.root) { AbstractViewHolder<Installable>(binding) {
lateinit var installable: Installable override fun bind(model: Installable) {
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
fun bind(installable: Installable) { if (model.install != null) {
this.installable = installable
binding.title.setText(installable.titleId)
binding.description.setText(installable.descriptionId)
if (installable.install != null) {
binding.buttonInstall.visibility = View.VISIBLE binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { installable.install.invoke() } binding.buttonInstall.setOnClickListener { model.install.invoke() }
} }
if (installable.export != null) { if (model.export != null) {
binding.buttonExport.visibility = View.VISIBLE binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { installable.export.invoke() } binding.buttonExport.setOnClickListener { model.export.invoke() }
} }
} }
} }

View File

@ -7,49 +7,33 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) : class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(), AbstractListAdapter<License, LicenseAdapter.LicenseViewHolder>(licenses) {
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
val binding = ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return LicenseViewHolder(it) }
binding.root.setOnClickListener(this)
return LicenseViewHolder(binding)
} }
override fun getItemCount(): Int = licenses.size inner class LicenseViewHolder(val binding: ListItemSettingBinding) :
AbstractViewHolder<License>(binding) {
override fun bind(model: License) {
binding.apply {
textSettingName.text = root.context.getString(model.titleId)
textSettingDescription.text = root.context.getString(model.descriptionId)
textSettingValue.visibility = View.GONE
override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) { root.setOnClickListener { onClick(model) }
holder.bind(licenses[position]) }
}
override fun onClick(view: View) {
val license = (view.tag as LicenseViewHolder).license
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
lateinit var license: License
init {
itemView.tag = this
} }
fun bind(license: License) { private fun onClick(license: License) {
this.license = license LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId)
binding.textSettingValue.visibility = View.GONE
} }
} }
} }

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
@ -18,31 +17,19 @@ import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) : class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() { AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SetupPageViewHolder(binding) .also { return SetupPageViewHolder(it) }
} }
override fun getItemCount(): Int = pages.size
override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) : inner class SetupPageViewHolder(val binding: PageSetupBinding) :
RecyclerView.ViewHolder(binding.root), SetupCallback { AbstractViewHolder<SetupPage>(binding), SetupCallback {
lateinit var page: SetupPage override fun bind(model: SetupPage) {
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
init {
itemView.tag = this
}
fun bind(page: SetupPage) {
this.page = page
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE binding.textConfirmation.visibility = View.VISIBLE
} }
@ -50,31 +37,31 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
page.iconId, model.iconId,
activity.theme activity.theme
) )
) )
binding.textTitle.text = activity.resources.getString(page.titleId) binding.textTitle.text = activity.resources.getString(model.titleId)
binding.textDescription.text = binding.textDescription.text =
Html.fromHtml(activity.resources.getString(page.descriptionId), 0) Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
binding.buttonAction.apply { binding.buttonAction.apply {
text = activity.resources.getString(page.buttonTextId) text = activity.resources.getString(model.buttonTextId)
if (page.buttonIconId != 0) { if (model.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable( icon = ResourcesCompat.getDrawable(
activity.resources, activity.resources,
page.buttonIconId, model.buttonIconId,
activity.theme activity.theme
) )
} }
iconGravity = iconGravity =
if (page.leftAlignedIcon) { if (model.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START MaterialButton.ICON_GRAVITY_START
} else { } else {
MaterialButton.ICON_GRAVITY_END MaterialButton.ICON_GRAVITY_END
} }
setOnClickListener { setOnClickListener {
page.buttonAction.invoke(this@SetupPageViewHolder) model.buttonAction.invoke(this@SetupPageViewHolder)
} }
} }
} }

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -13,20 +14,26 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -55,12 +62,43 @@ class DriverManagerFragment : Fragment() {
return binding.root return binding.root
} }
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true) homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
driverViewModel.onOpenDriverManager(args.game) driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.toolbarDrivers.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_driver_clear -> {
StringSetting.DRIVER_PATH.global = true
driverViewModel.updateDriverList()
(binding.listDrivers.adapter as DriverAdapter)
.replaceList(driverViewModel.driverList.value)
driverViewModel.showClearButton(false)
true
}
else -> false
}
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
driverViewModel.showClearButton.collect {
binding.toolbarDrivers.menu
.findItem(R.id.menu_driver_clear).isVisible = it
}
}
}
}
}
if (!driverViewModel.isInteractionAllowed.value) { if (!driverViewModel.isInteractionAllowed.value) {
DriversLoadingDialogFragment().show( DriversLoadingDialogFragment().show(
@ -85,25 +123,6 @@ class DriverManagerFragment : Fragment() {
adapter = DriverAdapter(driverViewModel) adapter = DriverAdapter(driverViewModel)
} }
viewLifecycleOwner.lifecycleScope.apply {
launch {
driverViewModel.driverList.collectLatest {
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
}
}
launch {
driverViewModel.newDriverInstalled.collect {
if (_binding != null && it) {
(binding.listDrivers.adapter as DriverAdapter).apply {
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
driverViewModel.setNewDriverInstalled(false)
}
}
}
}
}
setInsets() setInsets()
} }
@ -160,7 +179,7 @@ class DriverManagerFragment : Fragment() {
false false
) { ) {
val driverPath = val driverPath =
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}" "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
val driverFile = File(driverPath) val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip // Ignore file exceptions when a user selects an invalid zip
@ -177,12 +196,21 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList = val driverInList =
driverViewModel.driverList.value.firstOrNull { it.second == driverData } driverViewModel.driverData.firstOrNull { it.second == driverData }
if (driverInList != null) { if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed) return@newInstance getString(R.string.driver_already_installed)
} else { } else {
driverViewModel.addDriver(Pair(driverPath, driverData)) driverViewModel.onDriverAdded(Pair(driverPath, driverData))
driverViewModel.setNewDriverInstalled(true) withContext(Dispatchers.Main) {
if (_binding != null) {
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
}
}
} }
return@newInstance Any() return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
data class Driver(
override var selected: Boolean,
val title: String,
val version: String = "",
val description: String = ""
) : SelectableItem {
override fun onSelectionStateChanged(selected: Boolean) {
this.selected = selected
}
companion object {
fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver =
Driver(
selected,
this.name ?: "",
this.version ?: "",
this.description ?: ""
)
}
}

View File

@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -17,11 +18,10 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.BufferedOutputStream
import java.io.File import java.io.File
class DriverViewModel : ViewModel() { class DriverViewModel : ViewModel() {
@ -38,97 +38,81 @@ class DriverViewModel : ViewModel() {
!loading && ready && !deleting !loading && ready && !deleting
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers()) var driverData = GpuDriverHelper.getDrivers()
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
var previouslySelectedDriver = 0 private val _driverList = MutableStateFlow(emptyList<Driver>())
var selectedDriver = -1 val driverList: StateFlow<List<Driver>> get() = _driverList
// Used for showing which driver is currently installed within the driver manager card // Used for showing which driver is currently installed within the driver manager card
private val _selectedDriverTitle = MutableStateFlow("") private val _selectedDriverTitle = MutableStateFlow("")
val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
private val _newDriverInstalled = MutableStateFlow(false) private val _showClearButton = MutableStateFlow(false)
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled val showClearButton = _showClearButton.asStateFlow()
val driversToDelete = mutableListOf<String>() private val driversToDelete = mutableListOf<String>()
init { init {
val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData updateDriverList()
findSelectedDriver(currentDriverMetadata)
// If a user had installed a driver before the manager was implemented, this zips
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
// be indexed and exported as expected.
if (selectedDriver == -1) {
val driverToSave =
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
driverToSave.createNewFile()
FileUtil.zipFromInternalStorage(
File(GpuDriverHelper.driverInstallationPath!!),
GpuDriverHelper.driverInstallationPath!!,
BufferedOutputStream(driverToSave.outputStream())
)
_driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(_driverList.value.size - 1)
}
// If a user had installed a driver before the config was reworked to be multiplatform,
// we have save the path of the previously selected driver to the new setting.
if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 &&
StringSetting.DRIVER_PATH.global
) {
StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first)
NativeConfig.saveGlobalConfig()
} else {
findSelectedDriver(GpuDriverHelper.customDriverSettingData)
}
updateDriverNameForGame(null) updateDriverNameForGame(null)
} }
fun setSelectedDriverIndex(value: Int) { fun reloadDriverData() {
if (selectedDriver != -1) { _areDriversLoading.value = true
previouslySelectedDriver = selectedDriver driverData = GpuDriverHelper.getDrivers()
updateDriverList()
_areDriversLoading.value = false
}
fun updateDriverList() {
val selectedDriver = GpuDriverHelper.customDriverSettingData
val newDriverList = mutableListOf(
Driver(
selectedDriver == GpuDriverMetadata(),
YuzuApplication.appContext.getString(R.string.system_gpu_driver)
)
)
driverData.forEach {
newDriverList.add(it.second.toDriver(it.second == selectedDriver))
} }
selectedDriver = value _driverList.value = newDriverList
}
fun setNewDriverInstalled(value: Boolean) {
_newDriverInstalled.value = value
}
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
if (driverIndex == -1) {
_driverList.value.add(driverData)
setSelectedDriverIndex(_driverList.value.size - 1)
_selectedDriverTitle.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
}
}
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
} }
fun onOpenDriverManager(game: Game?) { fun onOpenDriverManager(game: Game?) {
if (game != null) { if (game != null) {
SettingsFile.loadCustomConfig(game) SettingsFile.loadCustomConfig(game)
} }
updateDriverList()
}
val driverPath = StringSetting.DRIVER_PATH.getString() fun showClearButton(value: Boolean) {
if (driverPath.isEmpty()) { _showClearButton.value = value
setSelectedDriverIndex(0) }
fun onDriverSelected(position: Int) {
if (position == 0) {
StringSetting.DRIVER_PATH.setString("")
} else { } else {
findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath))) StringSetting.DRIVER_PATH.setString(driverData[position - 1].first)
} }
} }
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
driversToDelete.add(driverData[removedPosition - 1].first)
driverData.removeAt(removedPosition - 1)
onDriverSelected(selectedPosition)
}
fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
if (driversToDelete.contains(driver.first)) {
driversToDelete.remove(driver.first)
}
driverData.add(driver)
onDriverSelected(driverData.size)
}
fun onCloseDriverManager(game: Game?) { fun onCloseDriverManager(game: Game?) {
_isDeletingDrivers.value = true _isDeletingDrivers.value = true
StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
updateDriverNameForGame(game) updateDriverNameForGame(game)
if (game == null) { if (game == null) {
NativeConfig.saveGlobalConfig() NativeConfig.saveGlobalConfig()
@ -181,20 +165,6 @@ class DriverViewModel : ViewModel() {
} }
} }
private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
if (driverList.value.size == 1) {
setSelectedDriverIndex(0)
return
}
driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
if (driver.second == currentDriverMetadata) {
setSelectedDriverIndex(i)
return
}
}
}
fun updateDriverNameForGame(game: Game?) { fun updateDriverNameForGame(game: Game?) {
if (!GpuDriverHelper.supportsCustomDriverLoading()) { if (!GpuDriverHelper.supportsCustomDriverLoading()) {
return return
@ -217,7 +187,6 @@ class DriverViewModel : ViewModel() {
private fun setDriverReady() { private fun setDriverReady() {
_isDriverReady.value = true _isDriverReady.value = true
_selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name updateName()
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} }
} }

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
interface SelectableItem {
var selected: Boolean
fun onSelectionStateChanged(selected: Boolean)
}

View File

@ -41,6 +41,7 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
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.model.AddonViewModel import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
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.TaskState
@ -58,6 +59,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val gamesViewModel: GamesViewModel by viewModels() private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels() private val taskViewModel: TaskViewModel by viewModels()
private val addonViewModel: AddonViewModel by viewModels() private val addonViewModel: AddonViewModel by viewModels()
private val driverViewModel: DriverViewModel by viewModels()
override var themeId: Int = 0 override var themeId: Int = 0
@ -689,6 +691,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig() NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false) gamesViewModel.reloadGames(false)
driverViewModel.reloadDriverData()
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)

View File

@ -62,9 +62,6 @@ object GpuDriverHelper {
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name } ?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct() ?.distinct()
?.toMutableList() ?: mutableListOf() ?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers return drivers
} }

View File

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.viewholder
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter
import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
/**
* [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a
* [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup.
*/
abstract class AbstractViewHolder<Model>(binding: ViewBinding) :
RecyclerView.ViewHolder(binding.root) {
abstract fun bind(model: Model)
}

View File

@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
handheld->Disconnect(); handheld->Disconnect();
} }
} }
@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
ASSERT(user_id); ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1,
FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0); user_id->AsU128(), 0);
const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
if (!Common::FS::CreateParentDirs(full_path)) { if (!Common::FS::CreateParentDirs(full_path)) {
@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
FileSys::Mode::Read); FileSys::Mode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
program_id, user_id->AsU128(), 0); program_id, user_id->AsU128(), 0);
return ToJString(env, user_save_data_path); return ToJString(env, user_save_data_path);
} }

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_driver_clear"
android:icon="@drawable/ic_clear"
android:title="@string/clear"
app:showAsAction="always" />
</menu>

View File

@ -8,6 +8,7 @@
#include "audio_core/sink/sink_stream.h" #include "audio_core/sink/sink_stream.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/guest_memory.h"
#include "core/memory.h" #include "core/memory.h"
namespace AudioCore { namespace AudioCore {

View File

@ -9,6 +9,7 @@
#include "common/fixed_point.h" #include "common/fixed_point.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "core/guest_memory.h"
#include "core/memory.h" #include "core/memory.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {

View File

@ -45,6 +45,7 @@ using f32 = float; ///< 32-bit floating point
using f64 = double; ///< 64-bit floating point using f64 = double; ///< 64-bit floating point
using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
using DAddr = u64; ///< Represents a pointer in the device specific virtual address space.
using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space. using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space. using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.

View File

@ -37,6 +37,8 @@ add_library(core STATIC
debugger/gdbstub_arch.h debugger/gdbstub_arch.h
debugger/gdbstub.cpp debugger/gdbstub.cpp
debugger/gdbstub.h debugger/gdbstub.h
device_memory_manager.h
device_memory_manager.inc
device_memory.cpp device_memory.cpp
device_memory.h device_memory.h
file_sys/fssystem/fs_i_storage.h file_sys/fssystem/fs_i_storage.h
@ -490,6 +492,10 @@ add_library(core STATIC
hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_pr.h
hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h hle/service/filesystem/fsp_srv.h
hle/service/filesystem/romfs_controller.cpp
hle/service/filesystem/romfs_controller.h
hle/service/filesystem/save_data_controller.cpp
hle/service/filesystem/save_data_controller.h
hle/service/fgm/fgm.cpp hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h hle/service/fgm/fgm.h
hle/service/friend/friend.cpp hle/service/friend/friend.cpp
@ -605,6 +611,8 @@ add_library(core STATIC
hle/service/ns/pdm_qry.h hle/service/ns/pdm_qry.h
hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp
hle/service/nvdrv/core/heap_mapper.h
hle/service/nvdrv/core/nvmap.cpp hle/service/nvdrv/core/nvmap.cpp
hle/service/nvdrv/core/nvmap.h hle/service/nvdrv/core/nvmap.h
hle/service/nvdrv/core/syncpoint_manager.cpp hle/service/nvdrv/core/syncpoint_manager.cpp

View File

@ -28,6 +28,7 @@
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_resource_limit.h"
@ -413,6 +414,7 @@ struct System::Impl {
kernel.ShutdownCores(); kernel.ShutdownCores();
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
telemetry_session.reset(); telemetry_session.reset();
time_manager.Shutdown(); time_manager.Shutdown();
@ -564,6 +566,9 @@ struct System::Impl {
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
gpu_dirty_memory_managers;
std::deque<std::vector<u8>> user_channel; std::deque<std::vector<u8>> user_channel;
}; };
@ -650,8 +655,14 @@ size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID(); return impl->kernel.GetCurrentHostThreadID();
} }
void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() {
return this->ApplicationProcess()->GatherGPUDirtyMemory(callback); return impl->gpu_dirty_memory_managers;
}
void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) {
for (auto& manager : impl->gpu_dirty_memory_managers) {
manager.Gather(callback);
}
} }
PerfStatsResults System::GetAndResetPerfStats() { PerfStatsResults System::GetAndResetPerfStats() {

View File

@ -8,6 +8,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <span>
#include <string> #include <string>
#include <vector> #include <vector>
@ -116,6 +117,7 @@ class CpuManager;
class Debugger; class Debugger;
class DeviceMemory; class DeviceMemory;
class ExclusiveMonitor; class ExclusiveMonitor;
class GPUDirtyMemoryManager;
class PerfStats; class PerfStats;
class Reporter; class Reporter;
class SpeedLimiter; class SpeedLimiter;
@ -224,7 +226,9 @@ public:
/// Prepare the core emulation for a reschedule /// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index); void PrepareReschedule(u32 core_index);
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); std::span<GPUDirtyMemoryManager> GetGPUDirtyMemoryManager();
void GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback);
[[nodiscard]] size_t GetCurrentHostThreadID() const; [[nodiscard]] size_t GetCurrentHostThreadID() const;

View File

@ -31,6 +31,12 @@ public:
DramMemoryMap::Base; DramMemoryMap::Base;
} }
template <typename T>
PAddr GetRawPhysicalAddr(const T* ptr) const {
return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) -
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer()));
}
template <typename T> template <typename T>
T* GetPointer(Common::PhysicalAddress addr) { T* GetPointer(Common::PhysicalAddress addr) {
return reinterpret_cast<T*>(buffer.BackingBasePointer() + return reinterpret_cast<T*>(buffer.BackingBasePointer() +
@ -43,6 +49,16 @@ public:
(GetInteger(addr) - DramMemoryMap::Base)); (GetInteger(addr) - DramMemoryMap::Base));
} }
template <typename T>
T* GetPointerFromRaw(PAddr addr) {
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
}
template <typename T>
const T* GetPointerFromRaw(PAddr addr) const {
return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr);
}
Common::HostMemory buffer; Common::HostMemory buffer;
}; };

View File

@ -0,0 +1,208 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <atomic>
#include <deque>
#include <memory>
#include <mutex>
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
constexpr size_t DEVICE_PAGESIZE = 1ULL << DEVICE_PAGEBITS;
constexpr size_t DEVICE_PAGEMASK = DEVICE_PAGESIZE - 1ULL;
class DeviceMemory;
namespace Memory {
class Memory;
}
template <typename DTraits>
struct DeviceMemoryManagerAllocator;
template <typename Traits>
class DeviceMemoryManager {
using DeviceInterface = typename Traits::DeviceInterface;
using DeviceMethods = typename Traits::DeviceMethods;
public:
DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager();
void BindInterface(DeviceInterface* device_inter);
DAddr Allocate(size_t size);
void AllocateFixed(DAddr start, size_t size);
void Free(DAddr start, size_t size);
void Map(DAddr address, VAddr virtual_address, size_t size, size_t process_id,
bool track = false);
void Unmap(DAddr address, size_t size);
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, size_t process_id);
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, size_t process_id) {
std::scoped_lock lk(mapping_guard);
TrackContinuityImpl(address, virtual_address, size, process_id);
}
// Write / Read
template <typename T>
T* GetPointer(DAddr address);
template <typename T>
const T* GetPointer(DAddr address) const;
template <typename Func>
void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) {
DAddr subbits = static_cast<DAddr>(address & page_mask);
const u32 base = compressed_device_addr[(address >> page_bits)];
if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] {
const DAddr d_address = (static_cast<DAddr>(base) << page_bits) + subbits;
operation(d_address);
return;
}
InnerGatherDeviceAddresses(buffer, address);
for (u32 value : buffer) {
operation((static_cast<DAddr>(value) << page_bits) + subbits);
}
}
template <typename Func>
void ApplyOpOnPointer(const u8* p, Common::ScratchBuffer<u32>& buffer, Func&& operation) {
PAddr address = GetRawPhysicalAddr<u8>(p);
ApplyOpOnPAddr(address, buffer, operation);
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = static_cast<PAddr>(address & page_mask);
auto paddr = compressed_physical_ptr[(address >> page_bits)];
if (paddr == 0) {
return 0;
}
return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits;
}
template <typename T>
void Write(DAddr address, T value);
template <typename T>
T Read(DAddr address) const;
u8* GetSpan(const DAddr src_addr, const std::size_t size);
const u8* GetSpan(const DAddr src_addr, const std::size_t size) const;
void ReadBlock(DAddr address, void* dest_pointer, size_t size);
void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size);
void WriteBlock(DAddr address, const void* src_pointer, size_t size);
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
size_t RegisterProcess(Memory::Memory* memory);
void UnregisterProcess(size_t id);
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
static constexpr size_t AS_BITS = Traits::device_virtual_bits;
private:
static constexpr size_t device_virtual_bits = Traits::device_virtual_bits;
static constexpr size_t device_as_size = 1ULL << device_virtual_bits;
static constexpr size_t physical_min_bits = 32;
static constexpr size_t physical_max_bits = 33;
static constexpr size_t page_bits = 12;
static constexpr size_t page_size = 1ULL << page_bits;
static constexpr size_t page_mask = page_size - 1ULL;
static constexpr u32 physical_address_base = 1U << page_bits;
static constexpr u32 MULTI_FLAG_BITS = 31;
static constexpr u32 MULTI_FLAG = 1U << MULTI_FLAG_BITS;
static constexpr u32 MULTI_MASK = ~MULTI_FLAG;
template <typename T>
T* GetPointerFromRaw(PAddr addr) {
return reinterpret_cast<T*>(physical_base + addr);
}
template <typename T>
const T* GetPointerFromRaw(PAddr addr) const {
return reinterpret_cast<T*>(physical_base + addr);
}
template <typename T>
PAddr GetRawPhysicalAddr(const T* ptr) const {
return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - physical_base);
}
void WalkBlock(const DAddr addr, const std::size_t size, auto on_unmapped, auto on_memory,
auto increment);
void InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, PAddr address);
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base;
DeviceInterface* device_inter;
Common::VirtualBuffer<u32> compressed_physical_ptr;
Common::VirtualBuffer<u32> compressed_device_addr;
Common::VirtualBuffer<u32> continuity_tracker;
// Process memory interfaces
std::deque<size_t> id_pool;
std::deque<Memory::Memory*> registered_processes;
// Memory protection management
static constexpr size_t guest_max_as_bits = 39;
static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits;
static constexpr size_t guest_mask = guest_as_size - 1ULL;
static constexpr size_t process_id_start_bit = guest_max_as_bits;
std::pair<size_t, VAddr> ExtractCPUBacking(size_t page_index) {
auto content = cpu_backing_address[page_index];
const VAddr address = content & guest_mask;
const size_t process_id = static_cast<size_t>(content >> process_id_start_bit);
return std::make_pair(process_id, address);
}
void InsertCPUBacking(size_t page_index, VAddr address, size_t process_id) {
cpu_backing_address[page_index] = address | (process_id << process_id_start_bit);
}
Common::VirtualBuffer<VAddr> cpu_backing_address;
static constexpr size_t subentries = 8 / sizeof(u8);
static constexpr size_t subentries_mask = subentries - 1;
class CounterEntry final {
public:
CounterEntry() = default;
std::atomic_uint8_t& Count(std::size_t page) {
return values[page & subentries_mask];
}
const std::atomic_uint8_t& Count(std::size_t page) const {
return values[page & subentries_mask];
}
private:
std::array<std::atomic_uint8_t, subentries> values{};
};
static_assert(sizeof(CounterEntry) == subentries * sizeof(u8),
"CounterEntry should be 8 bytes!");
static constexpr size_t num_counter_entries =
(1ULL << (device_virtual_bits - page_bits)) / subentries;
using CachedPages = std::array<CounterEntry, num_counter_entries>;
std::unique_ptr<CachedPages> cached_pages;
std::mutex counter_guard;
std::mutex mapping_guard;
};
} // namespace Core

View File

@ -0,0 +1,588 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
#include <limits>
#include <memory>
#include <type_traits>
#include "common/address_space.h"
#include "common/address_space.inc"
#include "common/alignment.h"
#include "common/assert.h"
#include "common/div_ceil.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/device_memory.h"
#include "core/device_memory_manager.h"
#include "core/memory.h"
namespace Core {
namespace {
class MultiAddressContainer {
public:
MultiAddressContainer() = default;
~MultiAddressContainer() = default;
void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) {
buffer.resize(8);
buffer.resize(0);
size_t index = 0;
const auto add_value = [&](u32 value) {
buffer[index] = value;
index++;
buffer.resize(index);
};
u32 iter_entry = start_entry;
Entry* current = &storage[iter_entry - 1];
add_value(current->value);
while (current->next_entry != 0) {
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
add_value(current->value);
}
}
u32 Register(u32 value) {
return RegisterImplementation(value);
}
void Register(u32 value, u32 start_entry) {
auto entry_id = RegisterImplementation(value);
u32 iter_entry = start_entry;
Entry* current = &storage[iter_entry - 1];
while (current->next_entry != 0) {
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
}
current->next_entry = entry_id;
}
std::pair<bool, u32> Unregister(u32 value, u32 start_entry) {
u32 iter_entry = start_entry;
Entry* previous{};
Entry* current = &storage[iter_entry - 1];
Entry* next{};
bool more_than_one_remaining = false;
u32 result_start{start_entry};
size_t count = 0;
while (current->value != value) {
count++;
previous = current;
iter_entry = current->next_entry;
current = &storage[iter_entry - 1];
}
// Find next
u32 next_entry = current->next_entry;
if (next_entry != 0) {
next = &storage[next_entry - 1];
more_than_one_remaining = next->next_entry != 0 || previous != nullptr;
}
if (previous) {
previous->next_entry = next_entry;
} else {
result_start = next_entry;
}
free_entries.emplace_back(iter_entry);
return std::make_pair(more_than_one_remaining || count > 1, result_start);
}
u32 ReleaseEntry(u32 start_entry) {
Entry* current = &storage[start_entry - 1];
free_entries.emplace_back(start_entry);
return current->value;
}
private:
u32 RegisterImplementation(u32 value) {
auto entry_id = GetNewEntry();
auto& entry = storage[entry_id - 1];
entry.next_entry = 0;
entry.value = value;
return entry_id;
}
u32 GetNewEntry() {
if (!free_entries.empty()) {
u32 result = free_entries.front();
free_entries.pop_front();
return result;
}
storage.emplace_back();
u32 new_entry = static_cast<u32>(storage.size());
return new_entry;
}
struct Entry {
u32 next_entry{};
u32 value{};
};
std::deque<Entry> storage;
std::deque<u32> free_entries;
};
struct EmptyAllocator {
EmptyAllocator([[maybe_unused]] DAddr address) {}
};
} // namespace
template <typename DTraits>
struct DeviceMemoryManagerAllocator {
static constexpr size_t device_virtual_bits = DTraits::device_virtual_bits;
static constexpr DAddr first_address = 1ULL << Memory::YUZU_PAGEBITS;
static constexpr DAddr max_device_area = 1ULL << device_virtual_bits;
DeviceMemoryManagerAllocator() : main_allocator(first_address) {}
Common::FlatAllocator<DAddr, 0, device_virtual_bits> main_allocator;
MultiAddressContainer multi_dev_address;
/// Returns true when vaddr -> vaddr+size is fully contained in the buffer
template <bool pin_area>
[[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept {
return addr >= 0 && addr + size <= max_device_area;
}
DAddr Allocate(size_t size) {
return main_allocator.Allocate(size);
}
void AllocateFixed(DAddr b_address, size_t b_size) {
main_allocator.AllocateFixed(b_address, b_size);
}
void Free(DAddr b_address, size_t b_size) {
main_allocator.Free(b_address, b_size);
}
};
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())},
device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS),
compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
Settings::MemoryLayout::Memory_4Gb
? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS)),
continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS),
cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
compressed_physical_ptr[i] = 0;
continuity_tracker[i] = 1;
cpu_backing_address[i] = 0;
}
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
Settings::MemoryLayout::Memory_4Gb
? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS);
for (size_t i = 0; i < total_phys; i++) {
compressed_device_addr[i] = 0;
}
}
template <typename Traits>
DeviceMemoryManager<Traits>::~DeviceMemoryManager() = default;
template <typename Traits>
void DeviceMemoryManager<Traits>::BindInterface(DeviceInterface* device_inter_) {
device_inter = device_inter_;
}
template <typename Traits>
DAddr DeviceMemoryManager<Traits>::Allocate(size_t size) {
return impl->Allocate(size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::AllocateFixed(DAddr start, size_t size) {
return impl->AllocateFixed(start, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
impl->Free(start, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size,
size_t process_id, bool track) {
Core::Memory::Memory* process_memory = registered_processes[process_id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) {
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] {
compressed_physical_ptr[start_page_d + i] = 0;
continue;
}
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
compressed_physical_ptr[start_page_d + i] = phys_addr;
InsertCPUBacking(start_page_d + i, new_vaddress, process_id);
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i);
if (base_dev == 0) [[likely]] {
compressed_device_addr[phys_addr - 1U] = new_dev;
continue;
}
u32 start_id = base_dev & MULTI_MASK;
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
start_id = impl->multi_dev_address.Register(base_dev);
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
}
impl->multi_dev_address.Register(new_dev, start_id);
}
if (track) {
TrackContinuityImpl(address, virtual_address, size, process_id);
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
device_inter->InvalidateRegion(address, size);
std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) {
auto phys_addr = compressed_physical_ptr[start_page_d + i];
compressed_physical_ptr[start_page_d + i] = 0;
cpu_backing_address[start_page_d + i] = 0;
if (phys_addr != 0) [[likely]] {
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
compressed_device_addr[phys_addr - 1] = 0;
continue;
}
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
if (!more_entries) {
compressed_device_addr[phys_addr - 1] =
impl->multi_dev_address.ReleaseEntry(new_start);
continue;
}
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
}
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
size_t size, size_t process_id) {
Core::Memory::Memory* process_memory = registered_processes[process_id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
uintptr_t last_ptr = 0;
size_t page_count = 1;
for (size_t i = num_pages; i > 0; i--) {
size_t index = i - 1;
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
const uintptr_t new_ptr = reinterpret_cast<uintptr_t>(
process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)));
if (new_ptr + page_size == last_ptr) {
page_count++;
} else {
page_count = 1;
}
last_ptr = new_ptr;
continuity_tracker[start_page_d + index] = static_cast<u32>(page_count);
}
}
template <typename Traits>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr);
}
return nullptr;
}
template <typename Traits>
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr);
}
return nullptr;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer,
PAddr address) {
size_t phys_addr = address >> page_bits;
std::scoped_lock lk(mapping_guard);
u32 backing = compressed_device_addr[phys_addr];
if ((backing >> MULTI_FLAG_BITS) != 0) {
impl->multi_dev_address.GatherValues(backing & MULTI_MASK, buffer);
return;
}
buffer.resize(1);
buffer[0] = backing;
}
template <typename Traits>
template <typename T>
T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
return nullptr;
}
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
}
template <typename Traits>
template <typename T>
const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
return nullptr;
}
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
}
template <typename Traits>
template <typename T>
void DeviceMemoryManager<Traits>::Write(DAddr address, T value) {
T* ptr = GetPointer<T>(address);
if (!ptr) [[unlikely]] {
return;
}
std::memcpy(ptr, &value, sizeof(T));
}
template <typename Traits>
template <typename T>
T DeviceMemoryManager<Traits>::Read(DAddr address) const {
const T* ptr = GetPointer<T>(address);
T result{};
if (!ptr) [[unlikely]] {
return result;
}
std::memcpy(&result, ptr, sizeof(T));
return result;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped,
auto on_memory, auto increment) {
std::size_t remaining_size = size;
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) {
const size_t next_pages = static_cast<std::size_t>(continuity_tracker[page_index]);
const std::size_t copy_amount =
std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT({
page_index += next_pages;
page_offset = 0;
increment(copy_amount);
remaining_size -= copy_amount;
});
auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr == 0) {
on_unmapped(copy_amount, current_vaddr);
continue;
}
auto* mem_ptr = GetPointerFromRaw<u8>(
(static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset);
on_memory(copy_amount, mem_ptr);
}
}
template <typename Traits>
void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, size_t size) {
device_inter->FlushRegion(address, size);
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
[&](size_t copy_amount, const u8* const src_ptr) {
std::memcpy(dest_pointer, src_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount;
});
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WriteBlock(DAddr address, const void* src_pointer, size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
std::memcpy(dst_ptr, src_pointer, copy_amount);
},
[&](const std::size_t copy_amount) {
src_pointer = static_cast<const u8*>(src_pointer) + copy_amount;
});
device_inter->InvalidateRegion(address, size);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
[&](size_t copy_amount, const u8* const src_ptr) {
std::memcpy(dest_pointer, src_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount;
});
}
template <typename Traits>
void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* src_pointer,
size_t size) {
WalkBlock(
address, size,
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
std::memcpy(dst_ptr, src_pointer, copy_amount);
},
[&](const std::size_t copy_amount) {
src_pointer = static_cast<const u8*>(src_pointer) + copy_amount;
});
}
template <typename Traits>
size_t DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
size_t new_id;
if (!id_pool.empty()) {
new_id = id_pool.front();
id_pool.pop_front();
registered_processes[new_id] = memory_device_inter;
} else {
registered_processes.emplace_back(memory_device_inter);
new_id = registered_processes.size() - 1U;
}
return new_id;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UnregisterProcess(size_t id) {
registered_processes[id] = nullptr;
id_pool.push_front(id);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
bool locked = false;
auto lock = [&] {
if (!locked) {
counter_guard.lock();
locked = true;
}
};
SCOPE_EXIT({
if (locked) {
counter_guard.unlock();
}
});
u64 uncache_begin = 0;
u64 cache_begin = 0;
u64 uncache_bytes = 0;
u64 cache_bytes = 0;
const auto MarkRegionCaching = &DeviceMemoryManager<Traits>::DeviceMethods::MarkRegionCaching;
std::atomic_thread_fence(std::memory_order_acquire);
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
size_t page = addr >> Memory::YUZU_PAGEBITS;
auto [process_id, base_vaddress] = ExtractCPUBacking(page);
size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
auto* memory_device_inter = registered_processes[process_id];
for (; page != page_end; ++page) {
std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page);
if (delta > 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(),
"Count may overflow!");
} else if (delta < 0) {
ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!");
} else {
ASSERT_MSG(false, "Delta must be non-zero!");
}
// Adds or subtracts 1, as count is a unsigned 8-bit value
count.fetch_add(static_cast<u8>(delta), std::memory_order_release);
// Assume delta is either -1 or 1
if (count.load(std::memory_order::relaxed) == 0) {
if (uncache_bytes == 0) {
uncache_begin = vpage;
}
uncache_bytes += Memory::YUZU_PAGESIZE;
} else if (uncache_bytes > 0) {
lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false);
uncache_bytes = 0;
}
if (count.load(std::memory_order::relaxed) == 1 && delta > 0) {
if (cache_bytes == 0) {
cache_begin = vpage;
}
cache_bytes += Memory::YUZU_PAGESIZE;
} else if (cache_bytes > 0) {
lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
cache_bytes = 0;
}
vpage++;
}
if (uncache_bytes > 0) {
lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
false);
}
if (cache_bytes > 0) {
lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true);
}
}
} // namespace Core

View File

@ -97,8 +97,9 @@ std::string SaveDataAttribute::DebugInfo() const {
static_cast<u8>(rank), index); static_cast<u8>(rank), index);
} }
SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
: dir{std::move(save_directory_)}, system{system_} { VirtualDir save_directory_)
: system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
// Delete all temporary storages // Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use. // On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp"); dir->DeleteSubdirectoryRecursive("temp");
@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
PrintSaveDataAttributeWarnings(meta); PrintSaveDataAttributeWarnings(meta);
const auto save_directory = const auto save_directory =
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
return dir->CreateDirectoryRelative(save_directory); return dir->CreateDirectoryRelative(save_directory);
} }
@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = const auto save_directory =
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
auto out = dir->GetDirectoryRelative(save_directory); auto out = dir->GetDirectoryRelative(save_directory);
@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
} }
} }
std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
SaveDataSpaceId space, SaveDataType type, u64 title_id, SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) { u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process. // be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
if (title_id == 0) { if (title_id == 0) {
title_id = system.GetApplicationProcessProgramID(); title_id = program_id;
} }
} }
@ -201,7 +202,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const { u128 user_id) const {
const auto path = const auto path =
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@ -220,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const { SaveDataSize new_value) const {
const auto path = const auto path =
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());

View File

@ -87,10 +87,13 @@ constexpr const char* GetSaveDataSizeFileName() {
return ".yuzu_save_size"; return ".yuzu_save_size";
} }
using ProgramId = u64;
/// File system interface to the SaveData archive /// File system interface to the SaveData archive
class SaveDataFactory { class SaveDataFactory {
public: public:
explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); explicit SaveDataFactory(Core::System& system_, ProgramId program_id_,
VirtualDir save_directory_);
~SaveDataFactory(); ~SaveDataFactory();
VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
@ -99,7 +102,7 @@ public:
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space,
SaveDataType type, u64 title_id, u128 user_id, u64 save_id); SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
@ -110,8 +113,9 @@ public:
void SetAutoCreate(bool state); void SetAutoCreate(bool state);
private: private:
VirtualDir dir;
Core::System& system; Core::System& system;
ProgramId program_id;
VirtualDir dir;
bool auto_create{true}; bool auto_create{true};
}; };

View File

@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
// Connect controllers based on the following priority list from highest to lowest priority: // Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) { if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
controller->Connect(true); controller->Connect(true);
} else if (parameters.allow_dual_joycons) { } else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);

View File

@ -10,7 +10,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "core/memory.h" #include "core/device_memory_manager.h"
namespace Core { namespace Core {
@ -23,7 +23,7 @@ public:
~GPUDirtyMemoryManager() = default; ~GPUDirtyMemoryManager() = default;
void Collect(VAddr address, size_t size) { void Collect(PAddr address, size_t size) {
TransformAddress t = BuildTransform(address, size); TransformAddress t = BuildTransform(address, size);
TransformAddress tmp, original; TransformAddress tmp, original;
do { do {
@ -47,7 +47,7 @@ public:
std::memory_order_relaxed)); std::memory_order_relaxed));
} }
void Gather(std::function<void(VAddr, size_t)>& callback) { void Gather(std::function<void(PAddr, size_t)>& callback) {
{ {
std::scoped_lock lk(guard); std::scoped_lock lk(guard);
TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed); TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed);
@ -65,7 +65,7 @@ public:
mask = mask >> empty_bits; mask = mask >> empty_bits;
const size_t continuous_bits = std::countr_one(mask); const size_t continuous_bits = std::countr_one(mask);
callback((static_cast<VAddr>(transform.address) << page_bits) + offset, callback((static_cast<PAddr>(transform.address) << page_bits) + offset,
continuous_bits << align_bits); continuous_bits << align_bits);
mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0; mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0;
offset += continuous_bits << align_bits; offset += continuous_bits << align_bits;
@ -80,7 +80,7 @@ private:
u32 mask; u32 mask;
}; };
constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1; constexpr static size_t page_bits = DEVICE_PAGEBITS - 1;
constexpr static size_t page_size = 1ULL << page_bits; constexpr static size_t page_size = 1ULL << page_bits;
constexpr static size_t page_mask = page_size - 1; constexpr static size_t page_mask = page_size - 1;
@ -89,7 +89,7 @@ private:
constexpr static size_t align_mask = align_size - 1; constexpr static size_t align_mask = align_size - 1;
constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U}; constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U};
bool IsValid(VAddr address) { bool IsValid(PAddr address) {
return address < (1ULL << 39); return address < (1ULL << 39);
} }
@ -103,7 +103,7 @@ private:
return mask; return mask;
} }
TransformAddress BuildTransform(VAddr address, size_t size) { TransformAddress BuildTransform(PAddr address, size_t size) {
const size_t minor_address = address & page_mask; const size_t minor_address = address & page_mask;
const size_t minor_bit = minor_address >> align_bits; const size_t minor_bit = minor_address >> align_bits;
const size_t top_bit = (minor_address + size + align_mask) >> align_bits; const size_t top_bit = (minor_address + size + align_mask) >> align_bits;

214
src/core/guest_memory.h Normal file
View File

@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <vector>
#include "common/assert.h"
#include "common/scratch_buffer.h"
namespace Core::Memory {
enum GuestMemoryFlags : u32 {
Read = 1 << 0,
Write = 1 << 1,
Safe = 1 << 2,
Cached = 1 << 3,
SafeRead = Read | Safe,
SafeWrite = Write | Safe,
SafeReadWrite = SafeRead | SafeWrite,
SafeReadCachedWrite = SafeReadWrite | Cached,
UnsafeRead = Read,
UnsafeWrite = Write,
UnsafeReadWrite = UnsafeRead | UnsafeWrite,
UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
};
namespace {
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemory {
using iterator = T*;
using const_iterator = const T*;
using value_type = T;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
public:
GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup);
}
}
~GuestMemory() = default;
T* data() noexcept {
return m_data_span.data();
}
const T* data() const noexcept {
return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
}
[[nodiscard]] T* begin() noexcept {
return this->data();
}
[[nodiscard]] const T* begin() const noexcept {
return this->data();
}
[[nodiscard]] T* end() noexcept {
return this->data() + this->size();
}
[[nodiscard]] const T* end() const noexcept {
return this->data() + this->size();
}
T& operator[](size_t index) noexcept {
return m_data_span[index];
}
const T& operator[](size_t index) const noexcept {
return m_data_span[index];
}
void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
m_addr = addr;
m_size = size;
m_addr_changed = true;
}
std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept {
m_addr = addr;
m_size = size;
if (m_size == 0) {
m_is_data_copy = true;
return {};
}
if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.FlushRegion(m_addr, this->size_bytes());
}
} else {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
}
m_is_data_copy = true;
m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else {
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
}
}
return m_data_span;
}
void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else {
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
}
}
bool TrySetSpan() noexcept {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true;
return true;
}
return false;
}
protected:
bool IsDataCopy() const noexcept {
return m_is_data_copy;
}
bool AddressChanged() const noexcept {
return m_addr_changed;
}
M& m_memory;
u64 m_addr{};
size_t m_size{};
std::span<T> m_data_span{};
std::vector<T> m_data_copy{};
bool m_span_valid{false};
bool m_is_data_copy{false};
bool m_addr_changed{false};
};
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public:
GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size() == 0) [[unlikely]] {
return;
}
if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
}
};
} // namespace
} // namespace Core::Memory

View File

@ -5,6 +5,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
@ -314,7 +315,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
// Ensure our memory is initialized. // Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this); m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager());
// Ensure we can insert the code region. // Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
@ -411,7 +412,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
// Ensure our memory is initialized. // Ensure our memory is initialized.
m_memory.SetCurrentPageTable(*this); m_memory.SetCurrentPageTable(*this);
m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager());
// Ensure we can insert the code region. // Ensure we can insert the code region.
R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
@ -1135,8 +1136,7 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
KProcess::KProcess(KernelCore& kernel) KProcess::KProcess(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
m_handle_table{kernel}, m_dirty_memory_managers{}, m_handle_table{kernel}, m_exclusive_monitor{}, m_memory{kernel.System()} {}
m_exclusive_monitor{}, m_memory{kernel.System()} {}
KProcess::~KProcess() = default; KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
@ -1318,10 +1318,4 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
return true; return true;
} }
void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
for (auto& manager : m_dirty_memory_managers) {
manager.Gather(callback);
}
}
} // namespace Kernel } // namespace Kernel

View File

@ -7,7 +7,6 @@
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h" #include "core/file_sys/program_metadata.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
@ -128,7 +127,6 @@ private:
#ifdef HAS_NCE #ifdef HAS_NCE
std::unordered_map<u64, u64> m_post_handlers{}; std::unordered_map<u64, u64> m_post_handlers{};
#endif #endif
std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor; std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
Core::Memory::Memory m_memory; Core::Memory::Memory m_memory;
@ -511,8 +509,6 @@ public:
return m_memory; return m_memory;
} }
void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
Core::ExclusiveMonitor& GetExclusiveMonitor() const { Core::ExclusiveMonitor& GetExclusiveMonitor() const {
return *m_exclusive_monitor; return *m_exclusive_monitor;
} }

View File

@ -36,6 +36,7 @@
#include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
attribute.type = FileSys::SaveDataType::SaveData; attribute.type = FileSys::SaveDataType::SaveData;
FileSys::VirtualDir save_data{}; FileSys::VirtualDir save_data{};
const auto res = system.GetFileSystemController().CreateSaveData( const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute); &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
"new_journal={:016X}", "new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
system.GetFileSystemController().WriteSaveDataSize( system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
type, system.GetApplicationProcessProgramID(), user_id, type, system.GetApplicationProcessProgramID(), user_id,
{new_normal_size, new_journal_size}); {new_normal_size, new_journal_size});
@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
user_id[0]); user_id[0]);
const auto size = system.GetFileSystemController().ReadSaveDataSize( const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
type, system.GetApplicationProcessProgramID(), user_id); type, system.GetApplicationProcessProgramID(), user_id);
IPC::ResponseBuilder rb{ctx, 6}; IPC::ResponseBuilder rb{ctx, 6};

View File

@ -24,15 +24,13 @@
#include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h" #include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
namespace Service::FileSystem { namespace Service::FileSystem {
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) { std::string_view dir_name_) {
std::string dir_name(Common::FS::SanitizePath(dir_name_)); std::string dir_name(Common::FS::SanitizePath(dir_name_));
@ -297,145 +295,65 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
FileSystemController::~FileSystemController() = default; FileSystemController::~FileSystemController() = default;
Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { Result FileSystemController::RegisterProcess(
romfs_factory = std::move(factory); ProcessId process_id, ProgramId program_id,
LOG_DEBUG(Service_FS, "Registered RomFS"); std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
std::scoped_lock lk{registration_lock};
registrations.emplace(process_id, Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
});
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
return ResultSuccess; return ResultSuccess;
} }
Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { Result FileSystemController::OpenProcess(
ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
save_data_factory = std::move(factory); std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
LOG_DEBUG(Service_FS, "Registered save data"); std::scoped_lock lk{registration_lock};
const auto it = registrations.find(process_id);
if (it == registrations.end()) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_program_id = it->second.program_id;
*out_save_data_controller =
std::make_shared<SaveDataController>(system, it->second.save_data_factory);
*out_romfs_controller =
std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
return ResultSuccess; return ResultSuccess;
} }
Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return ResultSuccess;
}
Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered BIS");
return ResultSuccess;
}
void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
LOG_TRACE(Service_FS, "Setting packed update for romfs"); LOG_TRACE(Service_FS, "Setting packed update for romfs");
if (romfs_factory == nullptr) std::scoped_lock lk{registration_lock};
const auto it = registrations.find(process_id);
if (it == registrations.end()) {
return; return;
}
romfs_factory->SetPackedUpdate(std::move(update_raw)); it->second.romfs_factory->SetPackedUpdate(std::move(update_raw));
} }
FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const { std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() {
LOG_TRACE(Service_FS, "Opening RomFS for current process"); return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{}));
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
} }
FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id, std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
FileSys::ContentRecordType type) const { ProgramId program_id) {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id); using YuzuPath = Common::FS::YuzuPath;
const auto rw_mode = FileSys::Mode::ReadWrite;
if (romfs_factory == nullptr) { auto vfs = system.GetFilesystem();
return nullptr; const auto nand_directory =
} vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
return romfs_factory->OpenPatchedRomFS(title_id, type); std::move(nand_directory));
}
FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex(
u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
program_index);
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
}
FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
title_id, storage_id, type);
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->Open(title_id, storage_id, type);
}
std::shared_ptr<FileSys::NCA> FileSystemController::OpenBaseNca(
u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
return romfs_factory->GetEntry(title_id, storage_id, type);
}
Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
save_struct.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
auto save_data = save_data_factory->Create(space, save_struct);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
attribute.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
auto save_data = save_data_factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) const {
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space);
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data_space = save_data_space;
return ResultSuccess;
} }
Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
return 0; return 0;
} }
FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
u64 title_id, u128 user_id) const {
if (save_data_factory == nullptr) {
return {0, 0};
}
const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
if (value.normal == 0 && value.journal == 0) {
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp_unique = metadata.first;
if (nacp_unique != nullptr) {
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
nacp_unique->GetDefaultJournalSaveSize()};
}
} else {
new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
}
WriteSaveDataSize(type, title_id, user_id, new_size);
return new_size;
}
return value;
}
void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) const {
if (save_data_factory != nullptr)
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
void FileSystemController::SetGameCard(FileSys::VirtualFile file) { void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
gamecard = std::make_unique<FileSys::XCI>(file); gamecard = std::make_unique<FileSys::XCI>(file);
const auto dir = gamecard->ConcatenatedPseudoDirectory(); const auto dir = gamecard->ConcatenatedPseudoDirectory();
@ -801,14 +677,9 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
return bis_factory->GetBCATDirectory(title_id); return bis_factory->GetBCATDirectory(title_id);
} }
void FileSystemController::SetAutoSaveDataCreation(bool enable) {
save_data_factory->SetAutoCreate(enable);
}
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) { if (overwrite) {
bis_factory = nullptr; bis_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr; sdmc_factory = nullptr;
} }
@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
bis_factory->GetUserNANDContents()); bis_factory->GetUserNANDContents());
} }
if (save_data_factory == nullptr) {
save_data_factory =
std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory));
}
if (sdmc_factory == nullptr) { if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
std::move(sd_load_directory)); std::move(sd_load_directory));
@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
} }
} }
void FileSystemController::Reset() {
std::scoped_lock lk{registration_lock};
registrations.clear();
}
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);
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
server_manager->RegisterNamedService("fsp-srv", std::make_shared<FSP_SRV>(system)); server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View File

@ -43,6 +43,9 @@ class ServiceManager;
namespace FileSystem { namespace FileSystem {
class RomFsController;
class SaveDataController;
enum class ContentStorageId : u32 { enum class ContentStorageId : u32 {
System, System,
User, User,
@ -61,32 +64,24 @@ enum class OpenDirectoryMode : u64 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
using ProcessId = u64;
using ProgramId = u64;
class FileSystemController { class FileSystemController {
public: public:
explicit FileSystemController(Core::System& system_); explicit FileSystemController(Core::System& system_);
~FileSystemController(); ~FileSystemController();
Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); Result RegisterProcess(ProcessId process_id, ProgramId program_id,
Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); std::shared_ptr<FileSys::RomFSFactory>&& factory);
Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); Result OpenProcess(ProgramId* out_program_id,
Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id);
void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
void SetPackedUpdate(FileSys::VirtualFile update_raw); std::shared_ptr<SaveDataController> OpenSaveDataController();
FileSys::VirtualFile OpenRomFSCurrentProcess() const;
FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const;
FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
FileSys::ContentRecordType type) const;
FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const;
Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const;
Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) const;
Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
FileSys::BisPartitionId id) const; FileSys::BisPartitionId id) const;
@ -96,11 +91,6 @@ public:
u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetFreeSpaceSize(FileSys::StorageId id) const;
u64 GetTotalSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const;
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
u128 user_id) const;
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) const;
void SetGameCard(FileSys::VirtualFile file); void SetGameCard(FileSys::VirtualFile file);
FileSys::XCI* GetGameCard() const; FileSys::XCI* GetGameCard() const;
@ -133,15 +123,24 @@ public:
FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
void SetAutoSaveDataCreation(bool enable);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called. // above is called.
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
void Reset();
private: private:
std::unique_ptr<FileSys::RomFSFactory> romfs_factory; std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id);
std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
struct Registration {
ProgramId program_id;
std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
};
std::mutex registration_lock;
std::map<ProcessId, Registration> registrations;
std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
std::unique_ptr<FileSys::BISFactory> bis_factory; std::unique_ptr<FileSys::BISFactory> bis_factory;

View File

@ -27,6 +27,8 @@
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h" #include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/hle_ipc.h" #include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/reporter.h" #include "core/reporter.h"
@ -577,9 +579,11 @@ private:
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public: public:
explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, explicit ISaveDataInfoReader(Core::System& system_,
FileSystemController& fsc_) std::shared_ptr<SaveDataController> save_data_controller_,
: ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { FileSys::SaveDataSpaceId space)
: ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{
save_data_controller_} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
}; };
@ -626,7 +630,7 @@ private:
void FindAllSaves(FileSys::SaveDataSpaceId space) { void FindAllSaves(FileSys::SaveDataSpaceId space) {
FileSys::VirtualDir save_root{}; FileSys::VirtualDir save_root{};
const auto result = fsc.OpenSaveDataSpace(&save_root, space); const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space);
if (result != ResultSuccess || save_root == nullptr) { if (result != ResultSuccess || save_root == nullptr) {
LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
@ -723,7 +727,8 @@ private:
}; };
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
FileSystemController& fsc; ProcessId process_id = 0;
std::shared_ptr<SaveDataController> save_data_controller;
std::vector<SaveDataInfo> info; std::vector<SaveDataInfo> info;
u64 next_entry_index = 0; u64 next_entry_index = 0;
}; };
@ -863,21 +868,20 @@ FSP_SRV::FSP_SRV(Core::System& system_)
if (Settings::values.enable_fs_access_log) { if (Settings::values.enable_fs_access_log) {
access_log_mode = AccessLogMode::SdCard; access_log_mode = AccessLogMode::SdCard;
} }
// This should be true on creation
fsc.SetAutoSaveDataCreation(true);
} }
FSP_SRV::~FSP_SRV() = default; FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; current_process_id = ctx.GetPID();
current_process_id = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
const auto res =
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(res);
} }
void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
@ -916,7 +920,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
uid[1], uid[0]); uid[1], uid[0]);
FileSys::VirtualDir save_data_dir{}; FileSys::VirtualDir save_data_dir{};
fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser,
save_struct);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -931,7 +936,8 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx)
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
FileSys::VirtualDir save_data_dir{}; FileSys::VirtualDir save_data_dir{};
fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem,
save_struct);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -950,7 +956,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
LOG_INFO(Service_FS, "called."); LOG_INFO(Service_FS, "called.");
FileSys::VirtualDir dir{}; FileSys::VirtualDir dir{};
auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); auto result =
save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
if (result != ResultSuccess) { if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0}; IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@ -1001,7 +1008,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>( rb.PushIpcInterface<ISaveDataInfoReader>(
std::make_shared<ISaveDataInfoReader>(system, space, fsc)); std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space));
} }
void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
@ -1009,8 +1016,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage, rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller,
fsc); FileSys::SaveDataSpaceId::TemporaryStorage);
} }
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
@ -1050,7 +1057,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
if (!romfs) { if (!romfs) {
auto current_romfs = fsc.OpenRomFSCurrentProcess(); auto current_romfs = romfs_controller->OpenRomFSCurrentProcess();
if (!current_romfs) { if (!current_romfs) {
// TODO (bunnei): Find the right error code to use here // TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!"); LOG_CRITICAL(Service_FS, "no file system interface available!");
@ -1078,7 +1085,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
storage_id, unknown, title_id); storage_id, unknown, title_id);
auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (!data) { if (!data) {
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
@ -1101,7 +1108,8 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
const FileSys::PatchManager pm{title_id, fsc, content_provider}; const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); auto base =
romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
auto storage = std::make_shared<IStorage>( auto storage = std::make_shared<IStorage>(
system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data));
@ -1129,9 +1137,8 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called, program_index={}", program_index); LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
auto patched_romfs = auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex(
fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, program_id, program_index, FileSys::ContentRecordType::Program);
FileSys::ContentRecordType::Program);
if (!patched_romfs) { if (!patched_romfs) {
// TODO: Find the right error code to use here // TODO: Find the right error code to use here
@ -1152,7 +1159,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
fsc.SetAutoSaveDataCreation(false); save_data_controller->SetAutoCreate(false);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -17,6 +17,9 @@ class FileSystemBackend;
namespace Service::FileSystem { namespace Service::FileSystem {
class RomFsController;
class SaveDataController;
enum class AccessLogVersion : u32 { enum class AccessLogVersion : u32 {
V7_0_0 = 2, V7_0_0 = 2,
@ -67,6 +70,9 @@ private:
u64 current_process_id = 0; u64 current_process_id = 0;
u32 access_log_program_index = 0; u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None; AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0;
std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller;
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/romfs_controller.h"
namespace Service::FileSystem {
RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_)
: factory{std::move(factory_)}, program_id{program_id_} {}
RomFsController::~RomFsController() = default;
FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() {
return factory->OpenCurrentProcess(program_id);
}
FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id,
FileSys::ContentRecordType type) {
return factory->OpenPatchedRomFS(title_id, type);
}
FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex(
u64 title_id, u8 program_index, FileSys::ContentRecordType type) {
return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
}
FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) {
return factory->Open(title_id, storage_id, type);
}
std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id,
FileSys::StorageId storage_id,
FileSys::ContentRecordType type) {
return factory->GetEntry(title_id, storage_id, type);
}
} // namespace Service::FileSystem

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_types.h"
namespace Service::FileSystem {
class RomFsController {
public:
explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_);
~RomFsController();
FileSys::VirtualFile OpenRomFSCurrentProcess();
FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type);
FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
FileSys::ContentRecordType type);
FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
private:
const std::shared_ptr<FileSys::RomFSFactory> factory;
const u64 program_id;
};
} // namespace Service::FileSystem

View File

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/loader/loader.h"
namespace Service::FileSystem {
namespace {
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SufficientSaveDataSize = 0xF0000000;
FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) {
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp = metadata.first;
if (nacp != nullptr) {
return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()};
}
return {SufficientSaveDataSize, SufficientSaveDataSize};
}
} // namespace
SaveDataController::SaveDataController(Core::System& system_,
std::shared_ptr<FileSys::SaveDataFactory> factory_)
: system{system_}, factory{std::move(factory_)} {}
SaveDataController::~SaveDataController() = default;
Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
attribute.DebugInfo());
auto save_data = factory->Create(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) {
auto save_data = factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) {
auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data_space = save_data_space;
return ResultSuccess;
}
FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
u128 user_id) {
const auto value = factory->ReadSaveDataSize(type, title_id, user_id);
if (value.normal == 0 && value.journal == 0) {
const auto size = GetDefaultSaveDataSize(system, title_id);
factory->WriteSaveDataSize(type, title_id, user_id, size);
return size;
}
return value;
}
void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) {
factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
void SaveDataController::SetAutoCreate(bool state) {
factory->SetAutoCreate(state);
}
} // namespace Service::FileSystem

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_types.h"
namespace Service::FileSystem {
class SaveDataController {
public:
explicit SaveDataController(Core::System& system,
std::shared_ptr<FileSys::SaveDataFactory> factory_);
~SaveDataController();
Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute);
Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute);
Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space);
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value);
void SetAutoCreate(bool state);
private:
Core::System& system;
const std::shared_ptr<FileSys::SaveDataFactory> factory;
};
} // namespace Service::FileSystem

View File

@ -1498,7 +1498,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
bool check_device_index = false; bool check_device_index = false;
switch (vibration_device_handle.npad_type) { switch (vibration_device_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController: case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconLeft:

View File

@ -12,6 +12,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "core/guest_memory.h"
#include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
@ -344,9 +345,9 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const { std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
static thread_local std::array read_buffer_a{ static thread_local std::array read_buffer_a{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
}; };
ASSERT_OR_EXECUTE_MSG( ASSERT_OR_EXECUTE_MSG(
@ -360,9 +361,9 @@ std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) con
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const { std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
static thread_local std::array read_buffer_x{ static thread_local std::array read_buffer_x{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
}; };
ASSERT_OR_EXECUTE_MSG( ASSERT_OR_EXECUTE_MSG(
@ -376,14 +377,14 @@ std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) con
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
static thread_local std::array read_buffer_a{ static thread_local std::array read_buffer_a{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
}; };
static thread_local std::array read_buffer_x{ static thread_local std::array read_buffer_x{
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
}; };
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&

View File

@ -41,6 +41,8 @@ class KernelCore;
class KHandleTable; class KHandleTable;
class KProcess; class KProcess;
class KServerSession; class KServerSession;
template <typename T>
class KScopedAutoObject;
class KThread; class KThread;
} // namespace Kernel } // namespace Kernel

View File

@ -2,27 +2,134 @@
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <atomic>
#include <deque>
#include <mutex>
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
Session::Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_)
: id{id_}, process{process_}, smmu_id{smmu_id_},
has_preallocated_area{}, mapper{}, is_active{} {}
Session::~Session() = default;
struct ContainerImpl { struct ContainerImpl {
explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) explicit ContainerImpl(Container& core, Tegra::Host1x::Host1x& host1x_)
: file{host1x_}, manager{host1x_}, device_file_data{} {} : host1x{host1x_}, file{core, host1x_}, manager{host1x_}, device_file_data{} {}
Tegra::Host1x::Host1x& host1x;
NvMap file; NvMap file;
SyncpointManager manager; SyncpointManager manager;
Container::Host1xDeviceFileData device_file_data; Container::Host1xDeviceFileData device_file_data;
std::deque<Session> sessions;
size_t new_ids{};
std::deque<size_t> id_pool;
std::mutex session_guard;
}; };
Container::Container(Tegra::Host1x::Host1x& host1x_) { Container::Container(Tegra::Host1x::Host1x& host1x_) {
impl = std::make_unique<ContainerImpl>(host1x_); impl = std::make_unique<ContainerImpl>(*this, host1x_);
} }
Container::~Container() = default; Container::~Container() = default;
size_t Container::OpenSession(Kernel::KProcess* process) {
std::scoped_lock lk(impl->session_guard);
for (auto& session : impl->sessions) {
if (!session.is_active) {
continue;
}
if (session.process == process) {
return session.id;
}
}
size_t new_id{};
auto* memory_interface = &process->GetMemory();
auto& smmu = impl->host1x.MemoryManager();
auto smmu_id = smmu.RegisterProcess(memory_interface);
if (!impl->id_pool.empty()) {
new_id = impl->id_pool.front();
impl->id_pool.pop_front();
impl->sessions[new_id] = Session{new_id, process, smmu_id};
} else {
new_id = impl->new_ids++;
impl->sessions.emplace_back(new_id, process, smmu_id);
}
auto& session = impl->sessions[new_id];
session.is_active = true;
// Optimization
if (process->IsApplication()) {
auto& page_table = process->GetPageTable().GetBasePageTable();
auto heap_start = page_table.GetHeapRegionStart();
Kernel::KProcessAddress cur_addr = heap_start;
size_t region_size = 0;
VAddr region_start = 0;
while (true) {
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo();
// check if this memory block is heap
if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
if (svc_mem_info.size > region_size) {
region_size = svc_mem_info.size;
region_start = svc_mem_info.base_address;
}
}
// Check if we're done.
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= GetInteger(cur_addr)) {
break;
}
cur_addr = next_address;
}
session.has_preallocated_area = false;
auto start_region = (region_size >> 15) >= 1024 ? smmu.Allocate(region_size) : 0;
if (start_region != 0) {
session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size,
smmu_id, impl->host1x);
smmu.TrackContinuity(start_region, region_start, region_size, smmu_id);
session.has_preallocated_area = true;
LOG_CRITICAL(Debug, "Preallocation created!");
}
}
return new_id;
}
void Container::CloseSession(size_t id) {
std::scoped_lock lk(impl->session_guard);
auto& session = impl->sessions[id];
auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart();
const size_t region_size = session.mapper->GetRegionSize();
session.mapper.reset();
smmu.Free(region_start, region_size);
session.has_preallocated_area = false;
}
session.is_active = false;
smmu.UnregisterProcess(impl->sessions[id].smmu_id);
impl->id_pool.emplace_front(id);
}
Session* Container::GetSession(size_t id) {
std::atomic_thread_fence(std::memory_order_acquire);
return &impl->sessions[id];
}
NvMap& Container::GetNvMapFile() { NvMap& Container::GetNvMapFile() {
return impl->file; return impl->file;
} }

View File

@ -10,22 +10,49 @@
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
namespace Kernel {
class KProcess;
}
namespace Tegra::Host1x { namespace Tegra::Host1x {
class Host1x; class Host1x;
} // namespace Tegra::Host1x } // namespace Tegra::Host1x
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
class HeapMapper;
class NvMap; class NvMap;
class SyncpointManager; class SyncpointManager;
struct ContainerImpl; struct ContainerImpl;
struct Session {
Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_);
~Session();
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
Session(Session&&) = default;
Session& operator=(Session&&) = default;
size_t id;
Kernel::KProcess* process;
size_t smmu_id;
bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{};
bool is_active{};
};
class Container { class Container {
public: public:
explicit Container(Tegra::Host1x::Host1x& host1x); explicit Container(Tegra::Host1x::Host1x& host1x);
~Container(); ~Container();
size_t OpenSession(Kernel::KProcess* process);
void CloseSession(size_t id);
Session* GetSession(size_t id);
NvMap& GetNvMapFile(); NvMap& GetNvMapFile();
const NvMap& GetNvMapFile() const; const NvMap& GetNvMapFile() const;

View File

@ -0,0 +1,175 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <mutex>
#include <boost/container/small_vector.hpp>
#define BOOST_NO_MT
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace Service::Nvidia::NvCore {
using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;
template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
// types
typedef counter_add_functor<Type> type;
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
// public member functions
void operator()(Type& current, const Type& added) const {
current += added;
if (current < base_type::identity_element()) {
current = base_type::identity_element();
}
}
// public static functions
static void version(Type&){};
};
using OverlapCombine = counter_add_functor<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default;
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
Func&& func) {
const DAddr start_address = cpu_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
};
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
Tegra::Host1x::Host1x& host1x)
: m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_smmu_id{smmu_id} {
m_internal = std::make_unique<HeapMapperInternal>(host1x);
}
HeapMapper::~HeapMapper() {
m_internal->device_memory.Unmap(m_daddress, m_size);
}
DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
const IntervalType interval{start, start + size};
m_internal->base_set.insert(interval);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.subtract(other);
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
m_smmu_id);
}
}
m_internal->mapping_overlaps += std::make_pair(interval, 1);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
}
void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int value) {
if (value <= 1) {
const IntervalType other{start_addr, end_addr};
m_internal->base_set.insert(other);
}
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
}
} // namespace Service::Nvidia::NvCore

View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <memory>
#include "common/common_types.h"
namespace Tegra::Host1x {
class Host1x;
} // namespace Tegra::Host1x
namespace Service::Nvidia::NvCore {
class HeapMapper {
public:
HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
Tegra::Host1x::Host1x& host1x);
~HeapMapper();
bool IsInBounds(VAddr start, size_t size) const {
VAddr end = start + size;
return start >= m_vaddress && end <= (m_vaddress + m_size);
}
DAddr Map(VAddr start, size_t size);
void Unmap(VAddr start, size_t size);
DAddr GetRegionStart() const {
return m_daddress;
}
size_t GetRegionSize() const {
return m_size;
}
private:
struct HeapMapperInternal;
VAddr m_vaddress;
DAddr m_daddress;
size_t m_size;
size_t m_smmu_id;
std::unique_ptr<HeapMapperInternal> m_internal;
};
} // namespace Service::Nvidia::NvCore

View File

@ -2,14 +2,19 @@
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <functional>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
using Core::Memory::YUZU_PAGESIZE; using Core::Memory::YUZU_PAGESIZE;
constexpr size_t BIG_PAGE_SIZE = YUZU_PAGESIZE * 16;
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
NvMap::Handle::Handle(u64 size_, Id id_) NvMap::Handle::Handle(u64 size_, Id id_)
@ -17,9 +22,8 @@ NvMap::Handle::Handle(u64 size_, Id id_)
flags.raw = 0; flags.raw = 0;
} }
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, size_t pSessionId) {
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
// Handles cannot be allocated twice // Handles cannot be allocated twice
if (allocated) { if (allocated) {
return NvResult::AccessDenied; return NvResult::AccessDenied;
@ -28,6 +32,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress)
flags = pFlags; flags = pFlags;
kind = pKind; kind = pKind;
align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
session_id = pSessionId;
// This flag is only applicable for handles with an address passed // This flag is only applicable for handles with an address passed
if (pAddress) { if (pAddress) {
@ -63,7 +68,7 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) {
return NvResult::Success; return NvResult::Success;
} }
NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {}
void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
std::scoped_lock lock(handles_lock); std::scoped_lock lock(handles_lock);
@ -78,12 +83,30 @@ void NvMap::UnmapHandle(Handle& handle_description) {
handle_description.unmap_queue_entry.reset(); handle_description.unmap_queue_entry.reset();
} }
// Free and unmap the handle from Host1x GMMU
if (handle_description.pin_virt_address) {
host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
handle_description.aligned_size);
host1x.Allocator().Free(handle_description.pin_virt_address,
static_cast<u32>(handle_description.aligned_size));
handle_description.pin_virt_address = 0;
}
// Free and unmap the handle from the SMMU // Free and unmap the handle from the SMMU
host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), const size_t map_size = handle_description.aligned_size;
handle_description.aligned_size); if (!handle_description.in_heap) {
host1x.Allocator().Free(handle_description.pin_virt_address, auto& smmu = host1x.MemoryManager();
static_cast<u32>(handle_description.aligned_size)); size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
handle_description.pin_virt_address = 0; smmu.Unmap(handle_description.d_address, map_size);
smmu.Free(handle_description.d_address, static_cast<size_t>(aligned_up));
handle_description.d_address = 0;
return;
}
const VAddr vaddress = handle_description.address;
auto* session = core.GetSession(handle_description.session_id);
session->mapper->Unmap(vaddress, map_size);
handle_description.d_address = 0;
handle_description.in_heap = false;
} }
bool NvMap::TryRemoveHandle(const Handle& handle_description) { bool NvMap::TryRemoveHandle(const Handle& handle_description) {
@ -124,22 +147,33 @@ std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
} }
} }
VAddr NvMap::GetHandleAddress(Handle::Id handle) { DAddr NvMap::GetHandleAddress(Handle::Id handle) {
std::scoped_lock lock(handles_lock); std::scoped_lock lock(handles_lock);
try { try {
return handles.at(handle)->address; return handles.at(handle)->d_address;
} catch (std::out_of_range&) { } catch (std::out_of_range&) {
return 0; return 0;
} }
} }
u32 NvMap::PinHandle(NvMap::Handle::Id handle) { DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
auto handle_description{GetHandle(handle)}; auto handle_description{GetHandle(handle)};
if (!handle_description) [[unlikely]] { if (!handle_description) [[unlikely]] {
return 0; return 0;
} }
std::scoped_lock lock(handle_description->mutex); std::scoped_lock lock(handle_description->mutex);
const auto map_low_area = [&] {
if (handle_description->pin_virt_address == 0) {
auto& gmmu_allocator = host1x.Allocator();
auto& gmmu = host1x.GMMU();
u32 address =
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
handle_description->aligned_size);
handle_description->pin_virt_address = address;
}
};
if (!handle_description->pins) { if (!handle_description->pins) {
// If we're in the unmap queue we can just remove ourselves and return since we're already // If we're in the unmap queue we can just remove ourselves and return since we're already
// mapped // mapped
@ -151,37 +185,58 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
unmap_queue.erase(*handle_description->unmap_queue_entry); unmap_queue.erase(*handle_description->unmap_queue_entry);
handle_description->unmap_queue_entry.reset(); handle_description->unmap_queue_entry.reset();
if (low_area_pin) {
map_low_area();
handle_description->pins++;
return static_cast<DAddr>(handle_description->pin_virt_address);
}
handle_description->pins++; handle_description->pins++;
return handle_description->pin_virt_address; return handle_description->d_address;
} }
} }
using namespace std::placeholders;
// If not then allocate some space and map it // If not then allocate some space and map it
u32 address{}; DAddr address{};
auto& smmu_allocator = host1x.Allocator(); auto& smmu = host1x.MemoryManager();
auto& smmu_memory_manager = host1x.MemoryManager(); auto* session = core.GetSession(handle_description->session_id);
while ((address = smmu_allocator.Allocate( const VAddr vaddress = handle_description->address;
static_cast<u32>(handle_description->aligned_size))) == 0) { const size_t map_size = handle_description->aligned_size;
// Free handles until the allocation succeeds if (session->has_preallocated_area && session->mapper->IsInBounds(vaddress, map_size)) {
std::scoped_lock queueLock(unmap_queue_lock); handle_description->d_address = session->mapper->Map(vaddress, map_size);
if (auto freeHandleDesc{unmap_queue.front()}) { handle_description->in_heap = true;
// Handles in the unmap queue are guaranteed not to be pinned so don't bother } else {
// checking if they are before unmapping size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
std::scoped_lock freeLock(freeHandleDesc->mutex); while ((address = smmu.Allocate(aligned_up)) == 0) {
if (handle_description->pin_virt_address) // Free handles until the allocation succeeds
UnmapHandle(*freeHandleDesc); std::scoped_lock queueLock(unmap_queue_lock);
} else { if (auto freeHandleDesc{unmap_queue.front()}) {
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); // Handles in the unmap queue are guaranteed not to be pinned so don't bother
// checking if they are before unmapping
std::scoped_lock freeLock(freeHandleDesc->mutex);
if (handle_description->d_address)
UnmapHandle(*freeHandleDesc);
} else {
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
}
} }
}
smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, handle_description->d_address = address;
handle_description->aligned_size); smmu.Map(address, vaddress, map_size, session->smmu_id, true);
handle_description->pin_virt_address = address; handle_description->in_heap = false;
}
}
if (low_area_pin) {
map_low_area();
} }
handle_description->pins++; handle_description->pins++;
return handle_description->pin_virt_address; if (low_area_pin) {
return static_cast<DAddr>(handle_description->pin_virt_address);
}
return handle_description->d_address;
} }
void NvMap::UnpinHandle(Handle::Id handle) { void NvMap::UnpinHandle(Handle::Id handle) {
@ -232,7 +287,7 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
} else if (handle_description->dupes == 0) { } else if (handle_description->dupes == 0) {
// Force unmap the handle // Force unmap the handle
if (handle_description->pin_virt_address) { if (handle_description->d_address) {
std::scoped_lock queueLock(unmap_queue_lock); std::scoped_lock queueLock(unmap_queue_lock);
UnmapHandle(*handle_description); UnmapHandle(*handle_description);
} }

View File

@ -25,6 +25,8 @@ class Host1x;
} // namespace Tegra } // namespace Tegra
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
class Container;
/** /**
* @brief The nvmap core class holds the global state for nvmap and provides methods to manage * @brief The nvmap core class holds the global state for nvmap and provides methods to manage
* handles * handles
@ -48,7 +50,7 @@ public:
using Id = u32; using Id = u32;
Id id; //!< A globally unique identifier for this handle Id id; //!< A globally unique identifier for this handle
s32 pins{}; s64 pins{};
u32 pin_virt_address{}; u32 pin_virt_address{};
std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
@ -61,15 +63,18 @@ public:
} flags{}; } flags{};
static_assert(sizeof(Flags) == sizeof(u32)); static_assert(sizeof(Flags) == sizeof(u32));
u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to,
//!< this can also be in the nvdrv tmem //!< this can also be in the nvdrv tmem
bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
//!< call //!< call
u8 kind{}; //!< Used for memory compression u8 kind{}; //!< Used for memory compression
bool allocated{}; //!< If the handle has been allocated with `Alloc` bool allocated{}; //!< If the handle has been allocated with `Alloc`
bool in_heap{};
size_t session_id{};
u64 dma_map_addr{}; //! remove me after implementing pinning. DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
//!< to, this can also be in the nvdrv tmem
Handle(u64 size, Id id); Handle(u64 size, Id id);
@ -77,7 +82,8 @@ public:
* @brief Sets up the handle with the given memory config, can allocate memory from the tmem * @brief Sets up the handle with the given memory config, can allocate memory from the tmem
* if a 0 address is passed * if a 0 address is passed
*/ */
[[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
size_t pSessionId);
/** /**
* @brief Increases the dupe counter of the handle for the given session * @brief Increases the dupe counter of the handle for the given session
@ -108,7 +114,7 @@ public:
bool can_unlock; //!< If the address region is ready to be unlocked bool can_unlock; //!< If the address region is ready to be unlocked
}; };
explicit NvMap(Tegra::Host1x::Host1x& host1x); explicit NvMap(Container& core, Tegra::Host1x::Host1x& host1x);
/** /**
* @brief Creates an unallocated handle of the given size * @brief Creates an unallocated handle of the given size
@ -117,7 +123,7 @@ public:
std::shared_ptr<Handle> GetHandle(Handle::Id handle); std::shared_ptr<Handle> GetHandle(Handle::Id handle);
VAddr GetHandleAddress(Handle::Id handle); DAddr GetHandleAddress(Handle::Id handle);
/** /**
* @brief Maps a handle into the SMMU address space * @brief Maps a handle into the SMMU address space
@ -125,7 +131,7 @@ public:
* number of calls to `UnpinHandle` * number of calls to `UnpinHandle`
* @return The SMMU virtual address that the handle has been mapped to * @return The SMMU virtual address that the handle has been mapped to
*/ */
u32 PinHandle(Handle::Id handle); DAddr PinHandle(Handle::Id handle, bool low_area_pin);
/** /**
* @brief When this has been called an equal number of times to `PinHandle` for the supplied * @brief When this has been called an equal number of times to `PinHandle` for the supplied
@ -172,5 +178,7 @@ private:
* @return If the handle was removed from the map * @return If the handle was removed from the map
*/ */
bool TryRemoveHandle(const Handle& handle_description); bool TryRemoveHandle(const Handle& handle_description);
Container& core;
}; };
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore

View File

@ -62,7 +62,7 @@ public:
* Called once a device is opened * Called once a device is opened
* @param fd The device fd * @param fd The device fd
*/ */
virtual void OnOpen(DeviceFD fd) = 0; virtual void OnOpen(size_t session_id, DeviceFD fd) = 0;
/** /**
* Called once a device is closed * Called once a device is closed

View File

@ -35,14 +35,14 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvdisp_disp0::OnOpen(DeviceFD fd) {} void nvdisp_disp0::OnOpen(size_t session_id, DeviceFD fd) {}
void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
u32 height, u32 stride, android::BufferTransformFlags transform, u32 height, u32 stride, android::BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, const Common::Rectangle<int>& crop_rect,
std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
const VAddr addr = nvmap.GetHandleAddress(buffer_handle); const DAddr addr = nvmap.GetHandleAddress(buffer_handle);
LOG_TRACE(Service, LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format); addr, offset, width, height, stride, format);

View File

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle. /// Performs a screen flip, drawing the buffer pointed to by the handle.

View File

@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_as_gpu::OnOpen(DeviceFD fd) {} void nvhost_as_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
@ -206,6 +206,8 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
static_cast<u32>(aligned_size >> page_size_bits)); static_cast<u32>(aligned_size >> page_size_bits));
} }
nvmap.UnpinHandle(mapping->handle);
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully // Only FreeSpace can unmap them fully
if (mapping->sparse_alloc) { if (mapping->sparse_alloc) {
@ -293,12 +295,12 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
return NvResult::BadValue; return NvResult::BadValue;
} }
VAddr cpu_address{static_cast<VAddr>( DAddr base = nvmap.PinHandle(entry.handle, false);
handle->address + DAddr device_address{static_cast<DAddr>(
(static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; base + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
gmmu->Map(virtual_address, cpu_address, size, static_cast<Tegra::PTEKind>(entry.kind), gmmu->Map(virtual_address, device_address, size,
use_big_pages); static_cast<Tegra::PTEKind>(entry.kind), use_big_pages);
} }
} }
@ -331,9 +333,9 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
} }
u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
VAddr cpu_address{mapping->ptr + params.buffer_offset}; VAddr device_address{mapping->ptr + params.buffer_offset};
gmmu->Map(gpu_address, cpu_address, params.mapping_size, gmmu->Map(gpu_address, device_address, params.mapping_size,
static_cast<Tegra::PTEKind>(params.kind), mapping->big_page); static_cast<Tegra::PTEKind>(params.kind), mapping->big_page);
return NvResult::Success; return NvResult::Success;
@ -349,7 +351,8 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; DAddr device_address{
static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)};
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
bool big_page{[&]() { bool big_page{[&]() {
@ -373,15 +376,14 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
} }
const bool use_big_pages = alloc->second.big_pages && big_page; const bool use_big_pages = alloc->second.big_pages && big_page;
gmmu->Map(params.offset, cpu_address, size, static_cast<Tegra::PTEKind>(params.kind), gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind),
use_big_pages); use_big_pages);
auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
use_big_pages, alloc->second.sparse)}; true, use_big_pages, alloc->second.sparse)};
alloc->second.mappings.push_back(mapping); alloc->second.mappings.push_back(mapping);
mapping_map[params.offset] = mapping; mapping_map[params.offset] = mapping;
} else { } else {
auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
@ -394,11 +396,11 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
return NvResult::InsufficientMemory; return NvResult::InsufficientMemory;
} }
gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size),
static_cast<Tegra::PTEKind>(params.kind), big_page); static_cast<Tegra::PTEKind>(params.kind), big_page);
auto mapping{ auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size,
std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; false, big_page, false)};
mapping_map[params.offset] = mapping; mapping_map[params.offset] = mapping;
} }
@ -433,6 +435,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
gmmu->Unmap(params.offset, mapping->size); gmmu->Unmap(params.offset, mapping->size);
} }
nvmap.UnpinHandle(mapping->handle);
mapping_map.erase(params.offset); mapping_map.erase(params.offset);
} catch (const std::out_of_range&) { } catch (const std::out_of_range&) {
LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset);

View File

@ -55,7 +55,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;
@ -159,16 +159,18 @@ private:
NvCore::NvMap& nvmap; NvCore::NvMap& nvmap;
struct Mapping { struct Mapping {
VAddr ptr; NvCore::NvMap::Handle::Id handle;
DAddr ptr;
u64 offset; u64 offset;
u64 size; u64 size;
bool fixed; bool fixed;
bool big_page; // Only valid if fixed == false bool big_page; // Only valid if fixed == false
bool sparse_alloc; bool sparse_alloc;
Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_,
: ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), bool big_page_, bool sparse_alloc_)
sparse_alloc(sparse_alloc_) {} : handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_),
big_page(big_page_), sparse_alloc(sparse_alloc_) {}
}; };
struct Allocation { struct Allocation {
@ -212,9 +214,6 @@ private:
bool initialised{}; bool initialised{};
} vm; } vm;
std::shared_ptr<Tegra::MemoryManager> gmmu; std::shared_ptr<Tegra::MemoryManager> gmmu;
// s32 channel{};
// u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -76,7 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inp
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_ctrl::OnOpen(DeviceFD fd) {} void nvhost_ctrl::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {}

View File

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} void nvhost_ctrl_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) { NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {

View File

@ -28,7 +28,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -120,7 +120,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_gpu::OnOpen(DeviceFD fd) {} void nvhost_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_gpu::OnClose(DeviceFD fd) {} void nvhost_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) { NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {

View File

@ -47,7 +47,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -35,7 +35,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
case 0x7: case 0x7:
return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
case 0x9: case 0x9:
return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output); return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd);
case 0xa: case 0xa:
return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
default: default:
@ -68,9 +68,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_nvdec::OnOpen(DeviceFD fd) { void nvhost_nvdec::OnOpen(size_t session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started"); LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true); system.SetNVDECActive(true);
sessions[fd] = session_id;
} }
void nvhost_nvdec::OnClose(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) {
@ -81,6 +82,10 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
system.GPU().ClearCdmaInstance(iter->second); system.GPU().ClearCdmaInstance(iter->second);
} }
system.SetNVDECActive(false); system.SetNVDECActive(false);
auto it = sessions.find(fd);
if (it != sessions.end()) {
sessions.erase(it);
}
} }
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -20,7 +20,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
}; };

View File

@ -8,6 +8,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h"
@ -95,6 +96,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU(); auto& gpu = system.GPU();
auto* session = core.GetSession(sessions[fd]);
if (gpu.UseNvdec()) { if (gpu.UseNvdec()) {
for (std::size_t i = 0; i < syncpt_increments.size(); i++) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i]; const SyncptIncr& syncpt_incr = syncpt_increments[i];
@ -106,8 +109,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
const auto object = nvmap.GetHandle(cmd_buffer.memory_id); const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.ApplicationMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32)); cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
} }
// Some games expect command_buffers to be written back // Some games expect command_buffers to be written back
@ -133,10 +136,12 @@ NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) { NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries,
DeviceFD fd) {
const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
for (size_t i = 0; i < num_entries; i++) { for (size_t i = 0; i < num_entries; i++) {
entries[i].map_address = nvmap.PinHandle(entries[i].map_handle); DAddr pin_address = nvmap.PinHandle(entries[i].map_handle, true);
entries[i].map_address = static_cast<u32>(pin_address);
} }
return NvResult::Success; return NvResult::Success;

View File

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <deque> #include <deque>
#include <unordered_map>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h"
@ -111,7 +113,7 @@ protected:
NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd); NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
NvResult GetSyncpoint(IoctlGetSyncpoint& params); NvResult GetSyncpoint(IoctlGetSyncpoint& params);
NvResult GetWaitbase(IoctlGetWaitbase& params); NvResult GetWaitbase(IoctlGetWaitbase& params);
NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, DeviceFD fd);
NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult SetSubmitTimeout(u32 timeout); NvResult SetSubmitTimeout(u32 timeout);
@ -125,6 +127,7 @@ protected:
NvCore::NvMap& nvmap; NvCore::NvMap& nvmap;
NvCore::ChannelType channel_type; NvCore::ChannelType channel_type;
std::array<u32, MaxSyncPoints> device_syncpoints{}; std::array<u32, MaxSyncPoints> device_syncpoints{};
std::unordered_map<DeviceFD, size_t> sessions;
}; };
}; // namespace Devices }; // namespace Devices
} // namespace Service::Nvidia } // namespace Service::Nvidia

View File

@ -44,7 +44,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_nvjpg::OnOpen(DeviceFD fd) {} void nvhost_nvjpg::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_nvjpg::OnClose(DeviceFD fd) {} void nvhost_nvjpg::OnClose(DeviceFD fd) {}
NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) { NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {

View File

@ -22,7 +22,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
private: private:

View File

@ -33,7 +33,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output); return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output);
case 0x9: case 0x9:
return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output); return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output, fd);
case 0xa: case 0xa:
return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output); return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output);
default: default:
@ -68,7 +68,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_vic::OnOpen(DeviceFD fd) {} void nvhost_vic::OnOpen(size_t session_id, DeviceFD fd) {
sessions[fd] = session_id;
}
void nvhost_vic::OnClose(DeviceFD fd) { void nvhost_vic::OnClose(DeviceFD fd) {
auto& host1x_file = core.Host1xDeviceFile(); auto& host1x_file = core.Host1xDeviceFile();
@ -76,6 +78,10 @@ void nvhost_vic::OnClose(DeviceFD fd) {
if (iter != host1x_file.fd_to_id.end()) { if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second); system.GPU().ClearCdmaInstance(iter->second);
} }
auto it = sessions.find(fd);
if (it != sessions.end()) {
sessions.erase(it);
}
} }
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -19,7 +19,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -36,9 +36,9 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
case 0x3: case 0x3:
return WrapFixed(this, &nvmap::IocFromId, input, output); return WrapFixed(this, &nvmap::IocFromId, input, output);
case 0x4: case 0x4:
return WrapFixed(this, &nvmap::IocAlloc, input, output); return WrapFixed(this, &nvmap::IocAlloc, input, output, fd);
case 0x5: case 0x5:
return WrapFixed(this, &nvmap::IocFree, input, output); return WrapFixed(this, &nvmap::IocFree, input, output, fd);
case 0x9: case 0x9:
return WrapFixed(this, &nvmap::IocParam, input, output); return WrapFixed(this, &nvmap::IocParam, input, output);
case 0xe: case 0xe:
@ -67,8 +67,15 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnOpen(size_t session_id, DeviceFD fd) {
void nvmap::OnClose(DeviceFD fd) {} sessions[fd] = session_id;
}
void nvmap::OnClose(DeviceFD fd) {
auto it = sessions.find(fd);
if (it != sessions.end()) {
sessions.erase(it);
}
}
NvResult nvmap::IocCreate(IocCreateParams& params) { NvResult nvmap::IocCreate(IocCreateParams& params) {
LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@ -87,7 +94,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocAlloc(IocAllocParams& params) { NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) {
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
if (!params.handle) { if (!params.handle) {
@ -116,15 +123,15 @@ NvResult nvmap::IocAlloc(IocAllocParams& params) {
return NvResult::InsufficientMemory; return NvResult::InsufficientMemory;
} }
const auto result = const auto result = handle_description->Alloc(params.flags, params.align, params.kind,
handle_description->Alloc(params.flags, params.align, params.kind, params.address); params.address, sessions[fd]);
if (result != NvResult::Success) { if (result != NvResult::Success) {
LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
return result; return result;
} }
bool is_out_io{}; bool is_out_io{};
ASSERT(system.ApplicationProcess() auto process = container.GetSession(sessions[fd])->process;
->GetPageTable() ASSERT(process->GetPageTable()
.LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
handle_description->size, handle_description->size,
Kernel::KMemoryPermission::None, true, false) Kernel::KMemoryPermission::None, true, false)
@ -224,7 +231,7 @@ NvResult nvmap::IocParam(IocParamParams& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocFree(IocFreeParams& params) { NvResult nvmap::IocFree(IocFreeParams& params, DeviceFD fd) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
if (!params.handle) { if (!params.handle) {
@ -233,9 +240,9 @@ NvResult nvmap::IocFree(IocFreeParams& params) {
} }
if (auto freeInfo{file.FreeHandle(params.handle, false)}) { if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
auto process = container.GetSession(sessions[fd])->process;
if (freeInfo->can_unlock) { if (freeInfo->can_unlock) {
ASSERT(system.ApplicationProcess() ASSERT(process->GetPageTable()
->GetPageTable()
.UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
.IsSuccess()); .IsSuccess());
} }

View File

@ -33,7 +33,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
enum class HandleParameterType : u32_le { enum class HandleParameterType : u32_le {
@ -100,11 +100,11 @@ public:
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
NvResult IocCreate(IocCreateParams& params); NvResult IocCreate(IocCreateParams& params);
NvResult IocAlloc(IocAllocParams& params); NvResult IocAlloc(IocAllocParams& params, DeviceFD fd);
NvResult IocGetId(IocGetIdParams& params); NvResult IocGetId(IocGetIdParams& params);
NvResult IocFromId(IocFromIdParams& params); NvResult IocFromId(IocFromIdParams& params);
NvResult IocParam(IocParamParams& params); NvResult IocParam(IocParamParams& params);
NvResult IocFree(IocFreeParams& params); NvResult IocFree(IocFreeParams& params, DeviceFD fd);
private: private:
/// Id to use for the next handle that is created. /// Id to use for the next handle that is created.
@ -115,6 +115,7 @@ private:
NvCore::Container& container; NvCore::Container& container;
NvCore::NvMap& file; NvCore::NvMap& file;
std::unordered_map<DeviceFD, size_t> sessions;
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -45,13 +45,22 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(system); auto module = std::make_shared<Module>(system);
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv")); const auto NvdrvInterfaceFactoryForApplication = [&, module] {
server_manager->RegisterNamedService("nvdrv:a", return std::make_shared<NVDRV>(system, module, "nvdrv");
std::make_shared<NVDRV>(system, module, "nvdrv:a")); };
server_manager->RegisterNamedService("nvdrv:s", const auto NvdrvInterfaceFactoryForApplets = [&, module] {
std::make_shared<NVDRV>(system, module, "nvdrv:s")); return std::make_shared<NVDRV>(system, module, "nvdrv:a");
server_manager->RegisterNamedService("nvdrv:t", };
std::make_shared<NVDRV>(system, module, "nvdrv:t")); const auto NvdrvInterfaceFactoryForSysmodules = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
};
const auto NvdrvInterfaceFactory = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:t");
};
server_manager->RegisterNamedService("nvdrv", NvdrvInterfaceFactoryForApplication);
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules);
server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactory);
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system)); server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
nvnflinger.SetNVDrvInstance(module); nvnflinger.SetNVDrvInstance(module);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
@ -113,7 +122,7 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
return NvResult::Success; return NvResult::Success;
} }
DeviceFD Module::Open(const std::string& device_name) { DeviceFD Module::Open(const std::string& device_name, size_t session_id) {
auto it = builders.find(device_name); auto it = builders.find(device_name);
if (it == builders.end()) { if (it == builders.end()) {
LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
@ -124,7 +133,7 @@ DeviceFD Module::Open(const std::string& device_name) {
auto& builder = it->second; auto& builder = it->second;
auto device = builder(fd)->second; auto device = builder(fd)->second;
device->OnOpen(fd); device->OnOpen(session_id, fd);
return fd; return fd;
} }

View File

@ -77,7 +77,7 @@ public:
NvResult VerifyFD(DeviceFD fd) const; NvResult VerifyFD(DeviceFD fd) const;
/// Opens a device node and returns a file descriptor to it. /// Opens a device node and returns a file descriptor to it.
DeviceFD Open(const std::string& device_name); DeviceFD Open(const std::string& device_name, size_t session_id);
/// Sends an ioctl command to the specified file descriptor. /// Sends an ioctl command to the specified file descriptor.
NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output); NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output);
@ -93,6 +93,10 @@ public:
NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
NvCore::Container& GetContainer() {
return container;
}
private: private:
friend class EventInterface; friend class EventInterface;
friend class Service::Nvnflinger::Nvnflinger; friend class Service::Nvnflinger::Nvnflinger;

View File

@ -3,8 +3,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
@ -37,7 +39,7 @@ void NVDRV::Open(HLERequestContext& ctx) {
return; return;
} }
DeviceFD fd = nvdrv->Open(device_name); DeviceFD fd = nvdrv->Open(device_name, session_id);
rb.Push<DeviceFD>(fd); rb.Push<DeviceFD>(fd);
rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
@ -150,12 +152,29 @@ void NVDRV::Close(HLERequestContext& ctx) {
void NVDRV::Initialize(HLERequestContext& ctx) { void NVDRV::Initialize(HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called"); LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
SCOPE_EXIT({
rb.Push(ResultSuccess);
rb.PushEnum(NvResult::Success);
});
if (is_initialized) {
// No need to initialize again
return;
}
IPC::RequestParser rp{ctx};
const auto process_handle{ctx.GetCopyHandle(0)};
// The transfer memory is lent to nvdrv as a work buffer since nvdrv is
// unable to allocate as much memory on its own. For HLE it's unnecessary to handle it
[[maybe_unused]] const auto transfer_memory_handle{ctx.GetCopyHandle(1)};
[[maybe_unused]] const auto transfer_memory_size = rp.Pop<u32>();
auto& container = nvdrv->GetContainer();
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle);
session_id = container.OpenSession(process.GetPointerUnsafe());
is_initialized = true; is_initialized = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(NvResult::Success);
} }
void NVDRV::QueryEvent(HLERequestContext& ctx) { void NVDRV::QueryEvent(HLERequestContext& ctx) {
@ -242,6 +261,9 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char*
RegisterHandlers(functions); RegisterHandlers(functions);
} }
NVDRV::~NVDRV() = default; NVDRV::~NVDRV() {
auto& container = nvdrv->GetContainer();
container.CloseSession(session_id);
}
} // namespace Service::Nvidia } // namespace Service::Nvidia

View File

@ -35,6 +35,7 @@ private:
u64 pid{}; u64 pid{};
bool is_initialized{}; bool is_initialized{};
size_t session_id{};
Common::ScratchBuffer<u8> output_buffer; Common::ScratchBuffer<u8> output_buffer;
Common::ScratchBuffer<u8> inline_output_buffer; Common::ScratchBuffer<u8> inline_output_buffer;
}; };

View File

@ -87,19 +87,20 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap,
R_SUCCEED(); R_SUCCEED();
} }
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) {
// Free the handle. // Free the handle.
Nvidia::Devices::nvmap::IocFreeParams free_params{ Nvidia::Devices::nvmap::IocFreeParams free_params{
.handle = handle, .handle = handle,
}; };
R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
u32 size) { u32 size, Nvidia::DeviceFD nvmap_fd) {
// Assign the allocated memory to the handle. // Assign the allocated memory to the handle.
Nvidia::Devices::nvmap::IocAllocParams alloc_params{ Nvidia::Devices::nvmap::IocAllocParams alloc_params{
.handle = handle, .handle = handle,
@ -109,16 +110,16 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
.kind = 0, .kind = 0,
.address = GetInteger(buffer), .address = GetInteger(buffer),
}; };
R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd,
Common::ProcessAddress buffer, u32 size) { Common::ProcessAddress buffer, u32 size) {
// Get the nvmap device. // Get the nvmap device.
auto nvmap_fd = nvdrv.Open("/dev/nvmap");
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
ASSERT(nvmap != nullptr); ASSERT(nvmap != nullptr);
@ -127,11 +128,11 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv,
// Ensure we maintain a clean state on failure. // Ensure we maintain a clean state on failure.
ON_RESULT_FAILURE { ON_RESULT_FAILURE {
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle))); ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)));
}; };
// Assign the allocated memory to the handle. // Assign the allocated memory to the handle.
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size)); R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
} }
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
@ -197,9 +198,13 @@ Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u
std::addressof(m_buffer_page_group), m_system, std::addressof(m_buffer_page_group), m_system,
SharedBufferSize)); SharedBufferSize));
auto& container = m_nvdrv->GetContainer();
m_session_id = container.OpenSession(m_system.ApplicationProcess());
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
// Create an nvmap handle for the buffer and assign the memory to it. // Create an nvmap handle for the buffer and assign the memory to it.
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address, R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
SharedBufferSize)); map_address, SharedBufferSize));
// Record the display id. // Record the display id.
m_display_id = display_id; m_display_id = display_id;

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h" #include "core/hle/service/nvnflinger/ui/fence.h"
@ -53,7 +54,8 @@ private:
u64 m_layer_id = 0; u64 m_layer_id = 0;
u32 m_buffer_nvmap_handle = 0; u32 m_buffer_nvmap_handle = 0;
SharedMemoryPoolLayout m_pool_layout = {}; SharedMemoryPoolLayout m_pool_layout = {};
Nvidia::DeviceFD m_nvmap_fd = {};
size_t m_session_id = {};
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
std::mutex m_guard; std::mutex m_guard;

View File

@ -126,7 +126,7 @@ void Nvnflinger::ShutdownLayers() {
void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
nvdrv = std::move(instance); nvdrv = std::move(instance);
disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); disp_fd = nvdrv->Open("/dev/nvdisp_disp0", 0);
} }
std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) { std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) {

View File

@ -22,11 +22,13 @@ GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
: NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) { : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
if (this->BufferId() > 0) { if (this->BufferId() > 0) {
m_nvmap->DuplicateHandle(this->BufferId(), true); m_nvmap->DuplicateHandle(this->BufferId(), true);
m_nvmap->PinHandle(this->BufferId(), false);
} }
} }
GraphicBuffer::~GraphicBuffer() { GraphicBuffer::~GraphicBuffer() {
if (m_nvmap != nullptr && this->BufferId() > 0) { if (m_nvmap != nullptr && this->BufferId() > 0) {
m_nvmap->UnpinHandle(this->BufferId());
m_nvmap->FreeHandle(this->BufferId(), true); m_nvmap->FreeHandle(this->BufferId(), true);
} }
} }

View File

@ -256,8 +256,13 @@ Result ServerManager::WaitAndProcessImpl() {
// Wait for a signal. // Wait for a signal.
s32 out_index{-1}; s32 out_index{-1};
R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
num_objs, -1)); wait_objs.data(), num_objs, -1)) {
R_CATCH(Kernel::ResultSessionClosed) {
// On session closed, index is updated and we don't want to return an error.
}
}
R_END_TRY_CATCH;
ASSERT(out_index >= 0 && out_index < num_objs); ASSERT(out_index >= 0 && out_index < num_objs);
// Set the output index. // Set the output index.

View File

@ -216,20 +216,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
} }
// Find the RomFS by searching for a ".romfs" file in this directory
const auto& files = dir->GetFiles();
const auto romfs_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
return f->GetName().find(".romfs") != std::string::npos;
});
// Register the RomFS if a ".romfs" file was found
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
romfs = *romfs_iter;
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
*this, system.GetContentProvider(), system.GetFileSystemController()));
}
is_loaded = true; is_loaded = true;
return {ResultStatus::Success, return {ResultStatus::Success,
LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};

View File

@ -74,8 +74,10 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
return load_result; return load_result;
} }
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( system.GetFileSystemController().RegisterProcess(
*this, system.GetContentProvider(), system.GetFileSystemController())); process.GetProcessId(), nca->GetTitleId(),
std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
system.GetFileSystemController()));
is_loaded = true; is_loaded = true;
return load_result; return load_result;

View File

@ -276,8 +276,10 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
} }
if (romfs != nullptr) { if (romfs != nullptr) {
system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( system.GetFileSystemController().RegisterProcess(
*this, system.GetContentProvider(), system.GetFileSystemController())); process.GetProcessId(), {},
std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
system.GetFileSystemController()));
} }
is_loaded = true; is_loaded = true;

View File

@ -111,7 +111,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
FileSys::VirtualFile update_raw; FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
std::move(update_raw));
} }
is_loaded = true; is_loaded = true;

View File

@ -78,7 +78,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S
FileSys::VirtualFile update_raw; FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
std::move(update_raw));
} }
is_loaded = true; is_loaded = true;

View File

@ -24,6 +24,8 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/host1x/host1x.h"
#include "video_core/rasterizer_download_area.h" #include "video_core/rasterizer_download_area.h"
namespace Core::Memory { namespace Core::Memory {
@ -637,17 +639,6 @@ struct Memory::Impl {
LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", GetInteger(target), LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", GetInteger(target),
base * YUZU_PAGESIZE, (base + size) * YUZU_PAGESIZE); base * YUZU_PAGESIZE, (base + size) * YUZU_PAGESIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
auto& gpu = system.GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
}
}
}
const auto end = base + size; const auto end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size()); base + page_table.pointers.size());
@ -811,21 +802,33 @@ struct Memory::Impl {
return true; return true;
} }
void HandleRasterizerDownload(VAddr address, size_t size) { void HandleRasterizerDownload(VAddr v_address, size_t size) {
const auto* p = GetPointerImpl(
v_address, []() {}, []() {});
if (!gpu_device_memory) [[unlikely]] {
gpu_device_memory = &system.Host1x().MemoryManager();
}
const size_t core = system.GetCurrentHostThreadID(); const size_t core = system.GetCurrentHostThreadID();
auto& current_area = rasterizer_read_areas[core]; auto& current_area = rasterizer_read_areas[core];
const VAddr end_address = address + size; gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
if (current_area.start_address <= address && end_address <= current_area.end_address) const DAddr end_address = address + size;
[[likely]] { if (current_area.start_address <= address && end_address <= current_area.end_address)
return; [[likely]] {
} return;
current_area = system.GPU().OnCPURead(address, size); }
current_area = system.GPU().OnCPURead(address, size);
});
} }
void HandleRasterizerWrite(VAddr address, size_t size) { void HandleRasterizerWrite(VAddr v_address, size_t size) {
const auto* p = GetPointerImpl(
v_address, []() {}, []() {});
constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
const size_t core = std::min(system.GetCurrentHostThreadID(), const size_t core = std::min(system.GetCurrentHostThreadID(),
sys_core); // any other calls threads go to syscore. sys_core); // any other calls threads go to syscore.
if (!gpu_device_memory) [[unlikely]] {
gpu_device_memory = &system.Host1x().MemoryManager();
}
// Guard on sys_core; // Guard on sys_core;
if (core == sys_core) [[unlikely]] { if (core == sys_core) [[unlikely]] {
sys_core_guard.lock(); sys_core_guard.lock();
@ -835,36 +838,53 @@ struct Memory::Impl {
sys_core_guard.unlock(); sys_core_guard.unlock();
} }
}); });
auto& current_area = rasterizer_write_areas[core]; gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
VAddr subaddress = address >> YUZU_PAGEBITS; auto& current_area = rasterizer_write_areas[core];
bool do_collection = current_area.last_address == subaddress; PAddr subaddress = address >> YUZU_PAGEBITS;
if (!do_collection) [[unlikely]] { bool do_collection = current_area.last_address == subaddress;
do_collection = system.GPU().OnCPUWrite(address, size); if (!do_collection) [[unlikely]] {
if (!do_collection) { do_collection = system.GPU().OnCPUWrite(address, size);
return; if (!do_collection) {
return;
}
current_area.last_address = subaddress;
} }
current_area.last_address = subaddress; gpu_dirty_managers[core].Collect(address, size);
} });
gpu_dirty_managers[core].Collect(address, size);
} }
struct GPUDirtyState { struct GPUDirtyState {
VAddr last_address; PAddr last_address;
}; };
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { void InvalidateGPUMemory(u8* p, size_t size) {
system.GPU().InvalidateRegion(GetInteger(dest_addr), size); constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
} const size_t core = std::min(system.GetCurrentHostThreadID(),
sys_core); // any other calls threads go to syscore.
void FlushRegion(Common::ProcessAddress dest_addr, size_t size) { if (!gpu_device_memory) [[unlikely]] {
system.GPU().FlushRegion(GetInteger(dest_addr), size); gpu_device_memory = &system.Host1x().MemoryManager();
}
// Guard on sys_core;
if (core == sys_core) [[unlikely]] {
sys_core_guard.lock();
}
SCOPE_EXIT({
if (core == sys_core) [[unlikely]] {
sys_core_guard.unlock();
}
});
auto& gpu = system.GPU();
gpu_device_memory->ApplyOpOnPointer(
p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
} }
Core::System& system; Core::System& system;
Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{};
Common::PageTable* current_page_table = nullptr; Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES>
rasterizer_read_areas{}; rasterizer_read_areas{};
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{};
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
std::mutex sys_core_guard; std::mutex sys_core_guard;
@ -1059,14 +1079,6 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug)
impl->MarkRegionDebug(GetInteger(vaddr), size, debug); impl->MarkRegionDebug(GetInteger(vaddr), size, debug);
} }
void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) {
impl->InvalidateRegion(dest_addr, size);
}
void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
impl->FlushRegion(dest_addr, size);
}
bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
[[maybe_unused]] bool mapped = true; [[maybe_unused]] bool mapped = true;
[[maybe_unused]] bool rasterizer = false; [[maybe_unused]] bool rasterizer = false;
@ -1078,10 +1090,10 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
GetInteger(vaddr)); GetInteger(vaddr));
mapped = false; mapped = false;
}, },
[&] { [&] { rasterizer = true; });
impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); if (rasterizer) {
rasterizer = true; impl->InvalidateGPUMemory(ptr, size);
}); }
#ifdef __linux__ #ifdef __linux__
if (!rasterizer && mapped) { if (!rasterizer && mapped) {

View File

@ -12,6 +12,7 @@
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/typed_address.h" #include "common/typed_address.h"
#include "core/guest_memory.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Common { namespace Common {
@ -486,10 +487,10 @@ public:
void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug);
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
bool InvalidateSeparateHeap(void* fault_address); bool InvalidateSeparateHeap(void* fault_address);
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
private: private:
Core::System& system; Core::System& system;
@ -498,209 +499,9 @@ private:
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
}; };
enum GuestMemoryFlags : u32 {
Read = 1 << 0,
Write = 1 << 1,
Safe = 1 << 2,
Cached = 1 << 3,
SafeRead = Read | Safe,
SafeWrite = Write | Safe,
SafeReadWrite = SafeRead | SafeWrite,
SafeReadCachedWrite = SafeReadWrite | Cached,
UnsafeRead = Read,
UnsafeWrite = Write,
UnsafeReadWrite = UnsafeRead | UnsafeWrite,
UnsafeReadCachedWrite = UnsafeReadWrite | Cached,
};
namespace {
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemory {
using iterator = T*;
using const_iterator = const T*;
using value_type = T;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
public:
GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: m_memory{memory}, m_addr{addr}, m_size{size} {
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
if constexpr (FLAGS & GuestMemoryFlags::Read) {
Read(addr, size, backup);
}
}
~GuestMemory() = default;
T* data() noexcept {
return m_data_span.data();
}
const T* data() const noexcept {
return m_data_span.data();
}
size_t size() const noexcept {
return m_size;
}
size_t size_bytes() const noexcept {
return this->size() * sizeof(T);
}
[[nodiscard]] T* begin() noexcept {
return this->data();
}
[[nodiscard]] const T* begin() const noexcept {
return this->data();
}
[[nodiscard]] T* end() noexcept {
return this->data() + this->size();
}
[[nodiscard]] const T* end() const noexcept {
return this->data() + this->size();
}
T& operator[](size_t index) noexcept {
return m_data_span[index];
}
const T& operator[](size_t index) const noexcept {
return m_data_span[index];
}
void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
m_addr = addr;
m_size = size;
m_addr_changed = true;
}
std::span<T> Read(u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) noexcept {
m_addr = addr;
m_size = size;
if (m_size == 0) {
m_is_data_copy = true;
return {};
}
if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.FlushRegion(m_addr, this->size_bytes());
}
} else {
if (backup) {
backup->resize_destructive(this->size());
m_data_span = *backup;
} else {
m_data_copy.resize(this->size());
m_data_span = std::span(m_data_copy);
}
m_is_data_copy = true;
m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
} else {
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
}
}
return m_data_span;
}
void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
} else {
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
}
}
bool TrySetSpan() noexcept {
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true;
return true;
}
return false;
}
protected:
bool IsDataCopy() const noexcept {
return m_is_data_copy;
}
bool AddressChanged() const noexcept {
return m_addr_changed;
}
M& m_memory;
u64 m_addr{};
size_t m_size{};
std::span<T> m_data_span{};
std::vector<T> m_data_copy{};
bool m_span_valid{false};
bool m_is_data_copy{false};
bool m_addr_changed{false};
};
template <typename M, typename T, GuestMemoryFlags FLAGS>
class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
public:
GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
if (!this->TrySetSpan()) {
if (backup) {
this->m_data_span = *backup;
this->m_span_valid = true;
this->m_is_data_copy = true;
}
}
}
}
~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) {
if (this->size() == 0) [[unlikely]] {
return;
}
if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
} else {
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
}
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
}
}
}
};
} // namespace
template <typename T, GuestMemoryFlags FLAGS> template <typename T, GuestMemoryFlags FLAGS>
using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; using CpuGuestMemory = GuestMemory<Core::Memory::Memory, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS> template <typename T, GuestMemoryFlags FLAGS>
using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; using CpuGuestMemoryScoped = GuestMemoryScoped<Core::Memory::Memory, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>;
template <typename T, GuestMemoryFlags FLAGS>
using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>;
} // namespace Core::Memory } // namespace Core::Memory

View File

@ -36,6 +36,30 @@ add_library(hid_core STATIC
irsensor/processor_base.h irsensor/processor_base.h
irsensor/tera_plugin_processor.cpp irsensor/tera_plugin_processor.cpp
irsensor/tera_plugin_processor.h irsensor/tera_plugin_processor.h
resources/abstracted_pad/abstract_battery_handler.cpp
resources/abstracted_pad/abstract_battery_handler.h
resources/abstracted_pad/abstract_button_handler.cpp
resources/abstracted_pad/abstract_button_handler.h
resources/abstracted_pad/abstract_ir_sensor_handler.cpp
resources/abstracted_pad/abstract_ir_sensor_handler.h
resources/abstracted_pad/abstract_led_handler.cpp
resources/abstracted_pad/abstract_led_handler.h
resources/abstracted_pad/abstract_mcu_handler.cpp
resources/abstracted_pad/abstract_mcu_handler.h
resources/abstracted_pad/abstract_nfc_handler.cpp
resources/abstracted_pad/abstract_nfc_handler.h
resources/abstracted_pad/abstract_pad.cpp
resources/abstracted_pad/abstract_pad.h
resources/abstracted_pad/abstract_pad_holder.cpp
resources/abstracted_pad/abstract_pad_holder.h
resources/abstracted_pad/abstract_palma_handler.cpp
resources/abstracted_pad/abstract_palma_handler.h
resources/abstracted_pad/abstract_properties_handler.cpp
resources/abstracted_pad/abstract_properties_handler.h
resources/abstracted_pad/abstract_sixaxis_handler.cpp
resources/abstracted_pad/abstract_sixaxis_handler.h
resources/abstracted_pad/abstract_vibration_handler.cpp
resources/abstracted_pad/abstract_vibration_handler.h
resources/debug_pad/debug_pad.cpp resources/debug_pad/debug_pad.cpp
resources/debug_pad/debug_pad.h resources/debug_pad/debug_pad.h
resources/debug_pad/debug_pad_types.h resources/debug_pad/debug_pad_types.h
@ -56,6 +80,8 @@ add_library(hid_core STATIC
resources/npad/npad_resource.cpp resources/npad/npad_resource.cpp
resources/npad/npad_resource.h resources/npad/npad_resource.h
resources/npad/npad_types.h resources/npad/npad_types.h
resources/npad/npad_vibration.cpp
resources/npad/npad_vibration.h
resources/palma/palma.cpp resources/palma/palma.cpp
resources/palma/palma.h resources/palma/palma.h
resources/six_axis/console_six_axis.cpp resources/six_axis/console_six_axis.cpp
@ -78,6 +104,14 @@ add_library(hid_core STATIC
resources/touch_screen/touch_types.h resources/touch_screen/touch_types.h
resources/unique_pad/unique_pad.cpp resources/unique_pad/unique_pad.cpp
resources/unique_pad/unique_pad.h resources/unique_pad/unique_pad.h
resources/vibration/gc_vibration_device.h
resources/vibration/gc_vibration_device.cpp
resources/vibration/n64_vibration_device.h
resources/vibration/n64_vibration_device.cpp
resources/vibration/vibration_base.h
resources/vibration/vibration_base.cpp
resources/vibration/vibration_device.h
resources/vibration/vibration_device.cpp
resources/applet_resource.cpp resources/applet_resource.cpp
resources/applet_resource.h resources/applet_resource.h
resources/controller_base.cpp resources/controller_base.cpp

View File

@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default;
NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
switch (type) { switch (type) {
case Settings::ControllerType::ProController: case Settings::ControllerType::ProController:
return NpadStyleIndex::ProController; return NpadStyleIndex::Fullkey;
case Settings::ControllerType::DualJoyconDetached: case Settings::ControllerType::DualJoyconDetached:
return NpadStyleIndex::JoyconDual; return NpadStyleIndex::JoyconDual;
case Settings::ControllerType::LeftJoycon: case Settings::ControllerType::LeftJoycon:
@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
case Settings::ControllerType::SegaGenesis: case Settings::ControllerType::SegaGenesis:
return NpadStyleIndex::SegaGenesis; return NpadStyleIndex::SegaGenesis;
default: default:
return NpadStyleIndex::ProController; return NpadStyleIndex::Fullkey;
} }
} }
Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
switch (type) { switch (type) {
case NpadStyleIndex::ProController: case NpadStyleIndex::Fullkey:
return Settings::ControllerType::ProController; return Settings::ControllerType::ProController;
case NpadStyleIndex::JoyconDual: case NpadStyleIndex::JoyconDual:
return Settings::ControllerType::DualJoyconDetached; return Settings::ControllerType::DualJoyconDetached;
@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
original_npad_type = npad_type; original_npad_type = npad_type;
} else { } else {
SetNpadStyleIndex(NpadStyleIndex::ProController); SetNpadStyleIndex(NpadStyleIndex::Fullkey);
original_npad_type = npad_type; original_npad_type = npad_type;
} }
@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
.body = GetNpadColor(controller.color_values[index].body), .body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons), .button = GetNpadColor(controller.color_values[index].buttons),
}; };
if (npad_type == NpadStyleIndex::ProController) { if (npad_type == NpadStyleIndex::Fullkey) {
controller.colors_state.left = { controller.colors_state.left = {
.body = GetNpadColor(controller.color_values[index].left_grip), .body = GetNpadColor(controller.color_values[index].left_grip),
.button = GetNpadColor(controller.color_values[index].buttons), .button = GetNpadColor(controller.color_values[index].buttons),
@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const {
switch (npad_type) { switch (npad_type) {
case NpadStyleIndex::JoyconRight: case NpadStyleIndex::JoyconRight:
case NpadStyleIndex::JoyconDual: case NpadStyleIndex::JoyconDual:
case NpadStyleIndex::ProController: case NpadStyleIndex::Fullkey:
case NpadStyleIndex::Handheld: case NpadStyleIndex::Handheld:
break; break;
default: default:
@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Fullkey controllers to Pro controllers // Fallback Fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) { if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::ProController); SetNpadStyleIndex(NpadStyleIndex::Fullkey);
Connect(); Connect();
return; return;
} }
@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Dual joycon controllers to Pro controllers // Fallback Dual joycon controllers to Pro controllers
if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::ProController); SetNpadStyleIndex(NpadStyleIndex::Fullkey);
Connect(); Connect();
return; return;
} }
// Fallback Pro controllers to Dual joycon // Fallback Pro controllers to Dual joycon
if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
SetNpadStyleIndex(NpadStyleIndex::JoyconDual); SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
Connect(); Connect();
@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) { switch (type) {
case NpadStyleIndex::ProController: case NpadStyleIndex::Fullkey:
case NpadStyleIndex::GameCube: case NpadStyleIndex::GameCube:
case NpadStyleIndex::NES: case NpadStyleIndex::NES:
case NpadStyleIndex::SNES: case NpadStyleIndex::SNES:
@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) { switch (type) {
case NpadStyleIndex::ProController: case NpadStyleIndex::Fullkey:
return supported_style_tag.fullkey.As<bool>(); return supported_style_tag.fullkey.As<bool>();
case NpadStyleIndex::Handheld: case NpadStyleIndex::Handheld:
return supported_style_tag.handheld.As<bool>(); return supported_style_tag.handheld.As<bool>();

View File

@ -220,6 +220,7 @@ enum class NpadIdType : u32 {
}; };
enum class NpadInterfaceType : u8 { enum class NpadInterfaceType : u8 {
None = 0,
Bluetooth = 1, Bluetooth = 1,
Rail = 2, Rail = 2,
Usb = 3, Usb = 3,
@ -229,7 +230,7 @@ enum class NpadInterfaceType : u8 {
// This is nn::hid::NpadStyleIndex // This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 { enum class NpadStyleIndex : u8 {
None = 0, None = 0,
ProController = 3, Fullkey = 3,
Handheld = 4, Handheld = 4,
HandheldNES = 4, HandheldNES = 4,
JoyconDual = 5, JoyconDual = 5,

View File

@ -42,7 +42,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand
constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
switch (handle.npad_type) { switch (handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController: case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconLeft:

View File

@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core_timing.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h"
#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/shared_memory_format.h"
namespace Service::HID {
NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {}
NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default;
void NpadAbstractBatteryHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
abstract_pad_holder = holder;
}
void NpadAbstractBatteryHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
applet_resource_holder = applet_resource;
}
void NpadAbstractBatteryHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
properties_handler = handler;
}
Result NpadAbstractBatteryHandler::IncrementRefCounter() {
if (ref_counter == std::numeric_limits<s32>::max() - 1) {
return ResultNpadHandlerOverflow;
}
ref_counter++;
return ResultSuccess;
}
Result NpadAbstractBatteryHandler::DecrementRefCounter() {
if (ref_counter == 0) {
return ResultNpadHandlerNotInitialized;
}
ref_counter--;
return ResultSuccess;
}
Result NpadAbstractBatteryHandler::UpdateBatteryState(u64 aruid) {
const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId());
AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid);
if (aruid_data == nullptr) {
return ResultSuccess;
}
auto& npad_internal_state =
aruid_data->shared_memory_format->npad.npad_entry[npad_index].internal_state;
auto& system_properties = npad_internal_state.system_properties;
system_properties.is_charging_joy_dual.Assign(dual_battery.is_charging);
system_properties.is_powered_joy_dual.Assign(dual_battery.is_powered);
system_properties.is_charging_joy_left.Assign(left_battery.is_charging);
system_properties.is_powered_joy_left.Assign(left_battery.is_powered);
system_properties.is_charging_joy_right.Assign(right_battery.is_charging);
system_properties.is_powered_joy_right.Assign(right_battery.is_powered);
npad_internal_state.battery_level_dual = dual_battery.battery_level;
npad_internal_state.battery_level_left = left_battery.battery_level;
npad_internal_state.battery_level_right = right_battery.battery_level;
return ResultSuccess;
}
void NpadAbstractBatteryHandler::UpdateBatteryState() {
if (ref_counter == 0) {
return;
}
has_new_battery_data = GetNewBatteryState();
}
bool NpadAbstractBatteryHandler::GetNewBatteryState() {
bool has_changed = false;
Core::HID::NpadPowerInfo new_dual_battery_state{};
Core::HID::NpadPowerInfo new_left_battery_state{};
Core::HID::NpadPowerInfo new_right_battery_state{};
std::array<IAbstractedPad*, 5> abstract_pads{};
const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
for (std::size_t i = 0; i < count; i++) {
auto* abstract_pad = abstract_pads[i];
if (!abstract_pad->internal_flags.is_connected) {
continue;
}
const auto power_info = abstract_pad->power_info;
if (power_info.battery_level > Core::HID::NpadBatteryLevel::Full) {
// Abort
continue;
}
const auto style = abstract_pad->assignment_style;
if (style.is_external_assigned || style.is_handheld_assigned) {
new_dual_battery_state = power_info;
}
if (style.is_external_left_assigned || style.is_handheld_left_assigned) {
new_left_battery_state = power_info;
}
if (style.is_external_right_assigned || style.is_handheld_right_assigned) {
new_right_battery_state = power_info;
}
if (abstract_pad->internal_flags.is_battery_low_ovln_required) {
if (abstract_pad->interface_type == Core::HID::NpadInterfaceType::Rail) {
// TODO
}
abstract_pad->internal_flags.is_battery_low_ovln_required.Assign(false);
}
}
if (dual_battery.battery_level != new_dual_battery_state.battery_level ||
dual_battery.is_charging != new_dual_battery_state.is_charging ||
dual_battery.is_powered != new_dual_battery_state.is_powered) {
has_changed = true;
dual_battery = new_dual_battery_state;
}
if (left_battery.battery_level != new_left_battery_state.battery_level ||
left_battery.is_charging != new_left_battery_state.is_charging ||
left_battery.is_powered != new_left_battery_state.is_powered) {
has_changed = true;
left_battery = new_left_battery_state;
}
if (right_battery.battery_level != new_right_battery_state.battery_level ||
right_battery.is_charging != new_right_battery_state.is_charging ||
right_battery.is_powered != new_right_battery_state.is_powered) {
has_changed = true;
right_battery = new_right_battery_state;
}
return has_changed;
}
void NpadAbstractBatteryHandler::UpdateCoreBatteryState() {
if (ref_counter == 0) {
return;
}
if (!has_new_battery_data) {
return;
}
UpdateBatteryState(0);
}
void NpadAbstractBatteryHandler::InitializeBatteryState(u64 aruid) {
UpdateBatteryState(aruid);
}
bool NpadAbstractBatteryHandler::HasBattery() const {
std::array<IAbstractedPad*, 5> abstract_pads{};
const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
for (std::size_t i = 0; i < count; i++) {
const auto* abstract_pad = abstract_pads[i];
if (!abstract_pad->internal_flags.is_connected) {
continue;
}
return abstract_pad->disabled_feature_set.has_fullkey_battery ||
abstract_pad->disabled_feature_set.has_left_right_joy_battery;
}
return false;
}
void NpadAbstractBatteryHandler::HasLeftRightBattery(bool& has_left, bool& has_right) const {
std::array<IAbstractedPad*, 5> abstract_pads{};
const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
has_left = false;
has_right = false;
for (std::size_t i = 0; i < count; i++) {
const auto* abstract_pad = abstract_pads[i];
if (!abstract_pad->internal_flags.is_connected) {
continue;
}
if (!abstract_pad->disabled_feature_set.has_fullkey_battery &&
!abstract_pad->disabled_feature_set.has_left_right_joy_battery) {
continue;
}
has_left = abstract_pad->assignment_style.is_external_left_assigned ||
abstract_pad->assignment_style.is_handheld_left_assigned;
has_right = abstract_pad->assignment_style.is_external_right_assigned ||
abstract_pad->assignment_style.is_handheld_right_assigned;
}
}
} // namespace Service::HID

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