Compare commits

...

43 Commits

Author SHA1 Message Date
ca25d1cb9e Android #132 2023-11-15 00:57:02 +00:00
5ca8629bb2 Merge PR 11889 2023-11-15 00:57:02 +00:00
c3e377a701 Merge PR 11535 2023-11-15 00:57:02 +00:00
24548b1f5c Merge pull request #12034 from t895/fps-outline
android: Add drop shadow to FPS counter
2023-11-14 18:20:24 -05:00
be0ecae108 Merge pull request #12035 from t895/pip-startup-shutdown-fix
android: Don't enter PiP during startup or shutdown
2023-11-14 18:20:18 -05:00
fcd54c6479 android: Don't enter PiP during startup or shutdown 2023-11-14 18:11:38 -05:00
08296f151e android: Add drop shadow to FPS counter
Also let the style control the text size
2023-11-14 17:53:40 -05:00
a134e924ff Merge pull request #12032 from liamwhite/fruit-compiler
fix apple clang build again
2023-11-14 12:23:28 -05:00
31ed6bae11 Merge pull request #12030 from t895/blocking-fix
android: Use suspend function for creating dynamic shortcuts
2023-11-14 12:23:17 -05:00
9a5ef835cc Merge pull request #12028 from liamwhite/coretiming-shutdown
core_timing: lock event queue access
2023-11-14 12:23:11 -05:00
df0d3698ae Merge pull request #12025 from liamwhite/kernel-shutdown-deadlock
core: check for thread dpc before eret
2023-11-14 12:23:04 -05:00
51fc608f68 Merge pull request #12019 from liamwhite/more-shutdown-deadlocks
audio_core: ignore renderer wait when stream is paused
2023-11-14 12:22:56 -05:00
b30e19ba24 fix apple clang build again 2023-11-14 11:53:02 -05:00
ec6b67d862 core_timing: lock event queue access 2023-11-14 11:51:04 -05:00
4d0b7f8496 android: Use suspend function for creating dynamic shortcuts
If the coil loader ever got stuck when creating a dynamic shortcut icon, the app would freeze. This would happen most notably when booting nca format games. This pushes that process to a separate coroutine that can be cancelled by the main activity's lifecycle.
2023-11-14 10:57:00 -05:00
e3b510a4b4 core: check for thread dpc before eret 2023-11-13 11:28:23 -05:00
247d66a680 Merge pull request #11990 from german77/audio
yuzu: Save mute when in background setting
2023-11-13 09:13:12 -05:00
0047d8a01e Merge pull request #12014 from t895/shrink-logo
android: Shrink branding images
2023-11-13 09:12:16 -05:00
efc0187537 Merge pull request #12015 from t895/remove-auto
android: Remove "auto" region option
2023-11-13 09:12:07 -05:00
b6fe8a0b3f Merge pull request #12018 from t895/settings-cleanup
android: Settings visual cleanup
2023-11-13 09:11:55 -05:00
ecaa038b4d audio_core: ignore renderer wait when stream is paused 2023-11-12 23:10:53 -05:00
4aac971864 android: Use the same transition animation between every fragment in settings
The animation that I used for entering search was prone to weird visual bugs and could appear visually jarring. This just makes things appear more consistent.
2023-11-12 21:36:51 -05:00
6c93cdffb1 android: Use more padding on top of each settings header 2023-11-12 21:36:51 -05:00
470714e2d1 android: Remove dividers between each setting 2023-11-12 21:36:51 -05:00
6b888b0fa8 android: Add icons and descriptions to the first-level advanced settings page
Additionally adjusts padding to place the icons in-line with the back button in the top app bar and makes the text for normal settings appear in-line with the expanded top app bar title.
2023-11-12 21:36:50 -05:00
1a1393dad7 android: Remove general section from settings
Limit speed options were moved to system, cpu accuracy was moved to debug, and PiP was moved to graphics.
2023-11-12 19:08:57 -05:00
55412962c0 android: Remove "auto" region option
This doesn't exist and if you clicked it, your region would be set to Taiwan.
2023-11-12 15:45:42 -05:00
d920da2631 android: Add a landscape-specific layout to the about page
Moves the logo to the side to fit more information on screen
2023-11-12 13:56:42 -05:00
ff72bf2cb2 android: Shrink logo in about page 2023-11-12 13:56:13 -05:00
4efb9763d9 android: Shrink logo in settings tab
Adjusts padding between the cards and logo to fit appropriately
2023-11-12 13:55:20 -05:00
c600bc8652 android: Fix top app bar tint being cut off in the about fragment
Adjust margin on the toolbar, not the app bar
2023-11-12 13:54:31 -05:00
767c4b5a99 Merge pull request #11980 from german77/moment
service: irs: Implement moment image processor
2023-11-11 20:02:23 -05:00
904d03b01f Merge pull request #12005 from german77/homebrew_id
yuzu: Keep homebrew on the recently played list
2023-11-11 20:02:09 -05:00
9f1c9599a2 Merge pull request #12004 from liamwhite/fix-hbl
k_capabilities: ignore map region when KTrace is disabled
2023-11-11 20:02:01 -05:00
5f6666a7cd Merge pull request #12003 from liamwhite/read-modules
gdbstub: read module information from memory layout
2023-11-11 20:01:53 -05:00
1906e2724f Merge pull request #11992 from t895/frame-check
android: Hide loading animation on first frame
2023-11-11 20:01:32 -05:00
0c032d3f2f yuzu: Keep homebrew on the recently played list 2023-11-11 10:14:11 -06:00
a6735cba5f k_capabilities: ignore map region when KTrace is disabled 2023-11-11 10:45:43 -05:00
3b872b89d1 gdbstub: read module information from memory layout 2023-11-11 10:41:06 -05:00
e588f341ed service: irs: Implement moment image processor 2023-11-11 00:28:12 -06:00
9e331f9957 yuzu: Make mute audio persistent 2023-11-10 10:22:04 -06:00
9169cbf728 yuzu: Save mute when in background setting 2023-11-10 10:22:02 -06:00
09f993899e android: Hide loading animation on first frame 2023-11-09 22:27:40 -05:00
135 changed files with 3758 additions and 3947 deletions

6
.gitmodules vendored
View File

@ -4,9 +4,6 @@
[submodule "enet"] [submodule "enet"]
path = externals/enet path = externals/enet
url = https://github.com/lsalzman/enet.git url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"] [submodule "cubeb"]
path = externals/cubeb path = externals/cubeb
url = https://github.com/mozilla/cubeb.git url = https://github.com/mozilla/cubeb.git
@ -61,3 +58,6 @@
[submodule "breakpad"] [submodule "breakpad"]
path = externals/breakpad path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git url = https://github.com/yuzu-emu/breakpad.git
[submodule "simpleini"]
path = externals/simpleini
url = https://github.com/brofield/simpleini.git

View File

@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
find_package(Boost 1.79.0 REQUIRED context) find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE) find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED) find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
find_package(lz4 REQUIRED) find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED)

View File

@ -1,27 +0,0 @@
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(INIH QUIET IMPORTED_TARGET inih)
if (INIReader IN_LIST inih_FIND_COMPONENTS)
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
if (INIREADER_FOUND)
set(inih_INIReader_FOUND TRUE)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(inih
REQUIRED_VARS INIH_LINK_LIBRARIES
VERSION_VAR INIH_VERSION
HANDLE_COMPONENTS
)
if (inih_FOUND AND NOT TARGET inih::inih)
add_library(inih::inih ALIAS PkgConfig::INIH)
endif()
if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader)
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
endif()

View File

@ -1,3 +1,13 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [11535](https://github.com/yuzu-emu/yuzu//pull/11535) | [`50bcfa5fb`](https://github.com/yuzu-emu/yuzu//pull/11535/files) | renderer_vulkan: Introduce separate cmd buffer for uploads | [GPUCode](https://github.com/GPUCode/) | Yes |
| [11889](https://github.com/yuzu-emu/yuzu//pull/11889) | [`a249b3018`](https://github.com/yuzu-emu/yuzu//pull/11889/files) | configuration: Unify config handling across frontends | [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

@ -34,11 +34,6 @@ endif()
# Glad # Glad
add_subdirectory(glad) add_subdirectory(glad)
# inih
if (NOT TARGET inih::INIReader)
add_subdirectory(inih)
endif()
# mbedtls # mbedtls
add_subdirectory(mbedtls) add_subdirectory(mbedtls)
target_include_directories(mbedtls PUBLIC ./mbedtls/include) target_include_directories(mbedtls PUBLIC ./mbedtls/include)
@ -295,3 +290,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB) target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
endif() endif()
endif() endif()
# SimpleIni
add_subdirectory(simpleini)

View File

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com>
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(inih
inih/ini.c
inih/ini.h
inih/cpp/INIReader.cpp
inih/cpp/INIReader.h
)
create_target_directory_groups(inih)
target_include_directories(inih INTERFACE inih/cpp)
add_library(inih::INIReader ALIAS inih)

1
externals/inih/inih vendored

Submodule externals/inih/inih deleted from 9cecf0643d

1
externals/simpleini vendored Submodule

Submodule externals/simpleini added at 382ddbb4b9

View File

@ -187,6 +187,7 @@ add_subdirectory(audio_core)
add_subdirectory(video_core) add_subdirectory(video_core)
add_subdirectory(network) add_subdirectory(network)
add_subdirectory(input_common) add_subdirectory(input_common)
add_subdirectory(frontend_common)
add_subdirectory(shader_recompiler) add_subdirectory(shader_recompiler)
if (YUZU_ROOM) if (YUZU_ROOM)

View File

@ -219,7 +219,6 @@ dependencies {
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")

View File

@ -230,8 +230,6 @@ object NativeLibrary {
*/ */
external fun onTouchReleased(finger_id: Int) external fun onTouchReleased(finger_id: Int)
external fun reloadSettings()
external fun initGameIni(gameID: String?) external fun initGameIni(gameID: String?)
external fun setAppDirectory(directory: String) external fun setAppDirectory(directory: String)

View File

@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val isEmulationActive = emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
pictureInPictureParamsBuilder.setAutoEnterEnabled( pictureInPictureParamsBuilder.setAutoEnterEnabled(
BooleanSetting.PICTURE_IN_PICTURE.boolean BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
) )
} }
setPictureInPictureParams(pictureInPictureParamsBuilder.build()) setPictureInPictureParams(pictureInPictureParamsBuilder.build())

View File

@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
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.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections 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
@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
data = Uri.parse(holder.game.path) data = Uri.parse(holder.game.path)
} }
val layerDrawable = ResourcesCompat.getDrawable( activity.lifecycleScope.launch {
YuzuApplication.appContext.resources, withContext(Dispatchers.IO) {
R.drawable.shortcut, val layerDrawable = ResourcesCompat.getDrawable(
null YuzuApplication.appContext.resources,
) as LayerDrawable R.drawable.shortcut,
layerDrawable.setDrawableByLayerId( null
R.id.shortcut_foreground, ) as LayerDrawable
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) layerDrawable.setDrawableByLayerId(
) R.id.shortcut_foreground,
val inset = YuzuApplication.appContext.resources GameIconUtils.getGameIcon(activity, holder.game)
.getDimensionPixelSize(R.dimen.icon_inset) .toDrawable(YuzuApplication.appContext.resources)
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)
) )
) val inset = YuzuApplication.appContext.resources
.setIntent(openIntent) .getDimensionPixelSize(R.dimen.icon_inset)
.build() layerDrawable.setLayerInset(1, inset, inset, inset, inset)
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) 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) val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
view.findNavController().navigate(action) view.findNavController().navigate(action)

View File

@ -7,7 +7,7 @@ import android.text.TextUtils
import android.widget.Toast import android.widget.Toast
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.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.utils.NativeConfig
object Settings { object Settings {
private val context get() = YuzuApplication.appContext private val context get() = YuzuApplication.appContext
@ -19,7 +19,7 @@ object Settings {
context.getString(R.string.ini_saved), context.getString(R.string.ini_saved),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) NativeConfig.saveSettings()
} else { } else {
// TODO: Save custom game settings // TODO: Save custom game settings
Toast.makeText( Toast.makeText(
@ -82,7 +82,6 @@ object Settings {
enum class MenuTag(val titleId: Int) { enum class MenuTag(val titleId: Int) {
SECTION_ROOT(R.string.advanced_settings), SECTION_ROOT(R.string.advanced_settings),
SECTION_GENERAL(R.string.preferences_general),
SECTION_SYSTEM(R.string.preferences_system), SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics), SECTION_RENDERER(R.string.preferences_graphics),
SECTION_AUDIO(R.string.preferences_audio), SECTION_AUDIO(R.string.preferences_audio),

View File

@ -3,10 +3,13 @@
package org.yuzu.yuzu_emu.features.settings.model.view package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
class RunnableSetting( class RunnableSetting(
titleId: Int, titleId: Int,
descriptionId: Int, descriptionId: Int,
val isRuntimeRunnable: Boolean, val isRuntimeRunnable: Boolean,
@DrawableRes val iconId: Int = 0,
val runnable: () -> Unit val runnable: () -> Unit
) : SettingsItem(emptySetting, titleId, descriptionId) { ) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_RUNNABLE override val type = TYPE_RUNNABLE

View File

@ -3,11 +3,14 @@
package org.yuzu.yuzu_emu.features.settings.model.view package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
class SubmenuSetting( class SubmenuSetting(
titleId: Int, @StringRes titleId: Int,
descriptionId: Int, @StringRes descriptionId: Int,
@DrawableRes val iconId: Int,
val menuKey: Settings.MenuTag val menuKey: Settings.MenuTag
) : SettingsItem(emptySetting, titleId, descriptionId) { ) : SettingsItem(emptySetting, titleId, descriptionId) {
override val type = TYPE_SUBMENU override val type = TYPE_SUBMENU

View File

@ -21,7 +21,6 @@ import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException import java.io.IOException
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
settingsViewModel.shouldSave = false settingsViewModel.shouldSave = false
// Delete settings file because the user may have changed values that do not exist in the UI // Delete settings file because the user may have changed values that do not exist in the UI
NativeConfig.unloadConfig()
val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
if (!settingsFile.delete()) { if (!settingsFile.delete()) {
throw IOException("Failed to delete $settingsFile") throw IOException("Failed to delete $settingsFile")
} }
NativeLibrary.reloadSettings() NativeConfig.initializeConfig()
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,

View File

@ -20,7 +20,6 @@ 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.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
) )
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
val dividerDecoration = MaterialDividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
)
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply { binding.listSettings.apply {
adapter = settingsAdapter adapter = settingsAdapter
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
addItemDecoration(dividerDecoration)
} }
binding.toolbarSettings.setNavigationOnClickListener { binding.toolbarSettings.setNavigationOnClickListener {
@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
} }
} }
} }
launch {
settingsViewModel.isUsingSearch.collectLatest {
if (it) {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
} else {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
}
}
}
} }
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
binding.toolbarSettings.setOnMenuItemClickListener { binding.toolbarSettings.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_search -> { R.id.action_search -> {
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
view.findNavController() view.findNavController()
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment) .navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
true true
@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
settingsViewModel.setIsUsingSearch(false)
}
private fun setInsets() { private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
mlpSettingsList.leftMargin = sideMargin + leftInsets mlpSettingsList.leftMargin = leftInsets
mlpSettingsList.rightMargin = sideMargin + rightInsets mlpSettingsList.rightMargin = rightInsets
binding.listSettings.layoutParams = mlpSettingsList binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding( binding.listSettings.updatePadding(
bottom = barInsets.bottom bottom = barInsets.bottom

View File

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
private val preferences: SharedPreferences private val preferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
private val context: Context get() = YuzuApplication.appContext
// Extension for populating settings list based on paired settings // Extension for populating settings list based on paired settings
fun ArrayList<SettingsItem>.add(key: String) { fun ArrayList<SettingsItem>.add(key: String) {
val item = SettingsItem.settingsItems[key]!! val item = SettingsItem.settingsItems[key]!!
@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
val sl = ArrayList<SettingsItem>() val sl = ArrayList<SettingsItem>()
when (menuTag) { when (menuTag) {
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
private fun addConfigSettings(sl: ArrayList<SettingsItem>) { private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
add( add(
RunnableSetting(R.string.reset_to_default, 0, false) { SubmenuSetting(
settingsViewModel.setShouldShowResetSettingsDialog(true) R.string.preferences_system,
} R.string.preferences_system_description,
R.drawable.ic_system_settings,
Settings.MenuTag.SECTION_SYSTEM
)
)
add(
SubmenuSetting(
R.string.preferences_graphics,
R.string.preferences_graphics_description,
R.drawable.ic_graphics,
Settings.MenuTag.SECTION_RENDERER
)
)
add(
SubmenuSetting(
R.string.preferences_audio,
R.string.preferences_audio_description,
R.drawable.ic_audio,
Settings.MenuTag.SECTION_AUDIO
)
)
add(
SubmenuSetting(
R.string.preferences_debug,
R.string.preferences_debug_description,
R.drawable.ic_code,
Settings.MenuTag.SECTION_DEBUG
)
)
add(
RunnableSetting(
R.string.reset_to_default,
R.string.reset_to_default_description,
false,
R.drawable.ic_restore
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
) )
}
}
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
} }
} }
private fun addSystemSettings(sl: ArrayList<SettingsItem>) { private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key) add(BooleanSetting.USE_DOCKED_MODE.key)
add(IntSetting.REGION_INDEX.key) add(IntSetting.REGION_INDEX.key)
add(IntSetting.LANGUAGE_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key)
@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_ANTI_ALIASING.key)
add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key) add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_DEBUG.key) add(BooleanSetting.RENDERER_DEBUG.key)
add(HeaderSetting(R.string.cpu)) add(HeaderSetting(R.string.cpu))
add(IntSetting.CPU_ACCURACY.key)
add(BooleanSetting.CPU_DEBUG_MODE.key) add(BooleanSetting.CPU_DEBUG_MODE.key)
add(SettingsItem.FASTMEM_COMBINED) add(SettingsItem.FASTMEM_COMBINED)
} }

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
setting = item as RunnableSetting setting = item as RunnableSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
override fun bind(item: SettingsItem) { override fun bind(item: SettingsItem) {
this.item = item as SubmenuSetting this.item = item as SubmenuSetting
if (item.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
item.iconId,
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.setText(item.nameId) binding.textSettingName.setText(item.nameId)
if (item.descriptionId != 0) { if (item.descriptionId != 0) {
binding.textSettingDescription.setText(item.descriptionId) binding.textSettingDescription.setText(item.descriptionId)

View File

@ -3,15 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.utils package org.yuzu.yuzu_emu.features.settings.utils
import android.widget.Toast
import java.io.* import java.io.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.*
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.NativeConfig
/** /**
* Contains static methods for interacting with .ini files in which settings are stored. * Contains static methods for interacting with .ini files in which settings are stored.
@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
object SettingsFile { object SettingsFile {
const val FILE_NAME_CONFIG = "config" const val FILE_NAME_CONFIG = "config"
/**
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
* telling why it failed.
*
* @param fileName The target filename without a path or extension.
*/
fun saveFile(fileName: String) {
val ini = getSettingsFile(fileName)
try {
val wini = Wini(ini)
for (specificCategory in Settings.Category.values()) {
val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
for (setting in Settings.settingsList) {
if (setting.key!!.isEmpty()) continue
val settingCategoryHeader =
NativeConfig.getConfigHeader(setting.category.ordinal)
val iniSetting: String? = wini.get(categoryHeader, setting.key)
if (iniSetting != null || settingCategoryHeader == categoryHeader) {
wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
}
}
}
wini.store()
} catch (e: IOException) {
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
val context = YuzuApplication.appContext
Toast.makeText(
context,
context.getString(R.string.error_saving, fileName, e.message),
Toast.LENGTH_SHORT
).show()
}
}
fun getSettingsFile(fileName: String): File = fun getSettingsFile(fileName: String): File =
File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
} }

View File

@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
val leftInsets = barInsets.left + cutoutInsets.left val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets mlpToolbar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets mlpToolbar.rightMargin = rightInsets
binding.appbarAbout.layoutParams = mlpAppBar binding.toolbarAbout.layoutParams = mlpToolbar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets mlpScrollAbout.leftMargin = leftInsets

View File

@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
} }
override fun onCreateView( override fun onCreateView(
@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
settingsViewModel.setIsUsingSearch(true)
if (savedInstanceState != null) { if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))

View File

@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
private val _shouldReloadSettingsList = MutableStateFlow(false) private val _shouldReloadSettingsList = MutableStateFlow(false)
val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
private val _isUsingSearch = MutableStateFlow(false)
val sliderProgress: StateFlow<Int> get() = _sliderProgress val sliderProgress: StateFlow<Int> get() = _sliderProgress
private val _sliderProgress = MutableStateFlow(-1) private val _sliderProgress = MutableStateFlow(-1)
@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
_shouldReloadSettingsList.value = value _shouldReloadSettingsList.value = value
} }
fun setIsUsingSearch(value: Boolean) {
_isUsingSearch.value = value
}
fun setSliderTextValue(value: Float, units: String) { fun setSliderTextValue(value: Float, units: String) {
_sliderProgress.value = value.toInt() _sliderProgress.value = value.toInt()
_sliderTextValue.value = String.format( _sliderTextValue.value = String.format(

View File

@ -632,6 +632,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
// Clear existing user data // Clear existing user data
NativeConfig.unloadConfig()
File(DirectoryInitialization.userDirectory!!).deleteRecursively() File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage // Copy archive to internal storage
@ -650,6 +651,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Reinitialize relevant data // Reinitialize relevant data
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem(true)
NativeConfig.initializeConfig()
gamesViewModel.reloadGames(false) gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success) return@newInstance getString(R.string.user_data_import_success)

View File

@ -16,6 +16,7 @@ object DirectoryInitialization {
if (!areDirectoriesReady) { if (!areDirectoriesReady) {
initializeInternalStorage() initializeInternalStorage()
NativeLibrary.initializeSystem(false) NativeLibrary.initializeSystem(false)
NativeConfig.initializeConfig()
areDirectoriesReady = true areDirectoriesReady = true
} }
} }

View File

@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
import android.widget.ImageView import android.widget.ImageView
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader import coil.ImageLoader
import coil.decode.DataSource import coil.decode.DataSource
import coil.executeBlocking
import coil.fetch.DrawableResult import coil.fetch.DrawableResult
import coil.fetch.FetchResult import coil.fetch.FetchResult
import coil.fetch.Fetcher import coil.fetch.Fetcher
@ -76,12 +76,13 @@ object GameIconUtils {
imageLoader.enqueue(request) imageLoader.enqueue(request)
} }
fun getGameIcon(game: Game): Bitmap { suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
val request = ImageRequest.Builder(YuzuApplication.appContext) val request = ImageRequest.Builder(YuzuApplication.appContext)
.data(game) .data(game)
.lifecycle(lifecycleOwner)
.error(R.drawable.default_icon) .error(R.drawable.default_icon)
.build() .build()
return imageLoader.executeBlocking(request) return imageLoader.execute(request)
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
} }
} }

View File

@ -4,6 +4,30 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
object NativeConfig { object NativeConfig {
/**
* Creates a Config object and opens the emulation config.
*/
@Synchronized
external fun initializeConfig()
/**
* Destroys the stored config object. This automatically saves the existing config.
*/
@Synchronized
external fun unloadConfig()
/**
* Reads values saved to the config file and saves them.
*/
@Synchronized
external fun reloadSettings()
/**
* Saves settings values in memory to disk.
*/
@Synchronized
external fun saveSettings()
external fun getBoolean(key: String, getDefault: Boolean): Boolean external fun getBoolean(key: String, getDefault: Boolean): Boolean
external fun setBoolean(key: String, value: Boolean) external fun setBoolean(key: String, value: Boolean)

View File

@ -6,9 +6,6 @@ add_library(yuzu-android SHARED
android_common/android_common.h android_common/android_common.h
applets/software_keyboard.cpp applets/software_keyboard.cpp
applets/software_keyboard.h applets/software_keyboard.h
config.cpp
config.h
default_ini.h
emu_window/emu_window.cpp emu_window/emu_window.cpp
emu_window/emu_window.h emu_window/emu_window.h
id_cache.cpp id_cache.cpp
@ -16,15 +13,17 @@ add_library(yuzu-android SHARED
native.cpp native.cpp
native.h native.h
native_config.cpp native_config.cpp
uisettings.cpp android_settings.cpp
game_metadata.cpp game_metadata.cpp
native_log.cpp native_log.cpp
android_config.cpp
android_config.h
) )
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
if (ARCHITECTURE_arm64) if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)
endif() endif()

View File

@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "android_config.h"
#include "android_settings.h"
#include "common/settings_setting.h"
AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type)
: Config(config_type) {
Initialize(config_name);
if (config_type != ConfigType::InputProfile) {
ReadAndroidValues();
SaveAndroidValues();
}
}
AndroidConfig::~AndroidConfig() {
if (global) {
AndroidConfig::SaveAllValues();
}
}
void AndroidConfig::ReloadAllValues() {
Reload();
ReadAndroidValues();
SaveAndroidValues();
}
void AndroidConfig::SaveAllValues() {
Save();
SaveAndroidValues();
}
void AndroidConfig::ReadAndroidValues() {
if (global) {
ReadAndroidUIValues();
}
}
void AndroidConfig::ReadAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
ReadCategory(Settings::Category::Android);
EndGroup();
}
void AndroidConfig::SaveAndroidValues() {
if (global) {
SaveAndroidUIValues();
}
WriteToIni();
}
void AndroidConfig::SaveAndroidUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
WriteCategory(Settings::Category::Android);
EndGroup();
}
std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
return Settings::values.linkage.by_category[category];
}
return AndroidSettings::values.linkage.by_category[category];
}

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "frontend_common/config.h"
class AndroidConfig final : public Config {
public:
explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~AndroidConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;
protected:
void ReadAndroidValues();
void ReadAndroidUIValues();
void ReadHidbusValues() override {}
void ReadDebugControlValues() override {}
void ReadPathValues() override {}
void ReadShortcutValues() override {}
void ReadUIValues() override {}
void ReadUIGamelistValues() override {}
void ReadUILayoutValues() override {}
void ReadMultiplayerValues() override {}
void SaveAndroidValues();
void SaveAndroidUIValues();
void SaveHidbusValues() override {}
void SaveDebugControlValues() override {}
void SavePathValues() override {}
void SaveShortcutValues() override {}
void SaveUIValues() override {}
void SaveUIGamelistValues() override {}
void SaveUILayoutValues() override {}
void SaveMultiplayerValues() override {}
std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) override;
};

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "uisettings.h" #include "android_settings.h"
namespace AndroidSettings { namespace AndroidSettings {

View File

@ -1,330 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <optional>
#include <sstream>
#include <INIReader.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/hle/service/acc/profile_manager.h"
#include "input_common/main.h"
#include "jni/config.h"
#include "jni/default_ini.h"
#include "uisettings.h"
namespace FS = Common::FS;
Config::Config(const std::string& config_name, ConfigType config_type)
: type(config_type), global{config_type == ConfigType::GlobalConfig} {
Initialize(config_name);
}
Config::~Config() = default;
bool Config::LoadINI(const std::string& default_contents, bool retry) {
void(FS::CreateParentDir(config_loc));
config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
const auto config_loc_str = FS::PathToUTF8String(config_loc);
if (config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
config_loc_str);
void(FS::CreateParentDir(config_loc));
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
config = std::make_unique<INIReader>(config_loc_str);
return LoadINI(default_contents, false);
}
LOG_ERROR(Config, "Failed.");
return false;
}
LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
return true;
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
if (setting_value.empty()) {
setting_value = setting.GetDefault();
}
setting = std::move(setting_value);
}
template <>
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
}
template <typename Type, bool ranged>
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
setting = static_cast<Type>(
config->GetInteger(group, setting.GetLabel(), static_cast<long>(setting.GetDefault())));
}
void Config::ReadValues() {
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
ReadSetting("ControlsGeneral", Settings::values.touch_device);
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
Settings::values.touchscreen.enabled =
config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.rotation_angle =
config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x =
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
Settings::values.touchscreen.diameter_y =
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
int num_touch_from_button_maps =
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) {
Settings::TouchFromButtonMap map;
map.name = config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_name"),
"default");
const int num_touch_maps = config->GetInteger(
"ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
0);
map.buttons.reserve(num_touch_maps);
for (int j = 0; j < num_touch_maps; ++j) {
std::string touch_mapping =
config->Get("ControlsGeneral",
std::string("touch_from_button_maps_") + std::to_string(i) +
std::string("_bind_") + std::to_string(j),
"");
map.buttons.emplace_back(std::move(touch_mapping));
}
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
}
} else {
Settings::values.touch_from_button_maps.emplace_back(
Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1;
}
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
// Data Storage
ReadSetting("Data Storage", Settings::values.use_virtual_sd);
FS::SetYuzuPath(FS::YuzuPath::NANDDir,
config->Get("Data Storage", "nand_directory",
FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
config->Get("Data Storage", "sdmc_directory",
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
FS::SetYuzuPath(FS::YuzuPath::LoadDir,
config->Get("Data Storage", "load_directory",
FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
FS::SetYuzuPath(FS::YuzuPath::DumpDir,
config->Get("Data Storage", "dump_directory",
FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
ReadSetting("Data Storage", Settings::values.gamecard_inserted);
ReadSetting("Data Storage", Settings::values.gamecard_current_game);
ReadSetting("Data Storage", Settings::values.gamecard_path);
// System
ReadSetting("System", Settings::values.current_user);
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
Service::Account::MAX_USERS - 1);
// Disable docked mode by default on Android
Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
? Settings::ConsoleMode::Docked
: Settings::ConsoleMode::Handheld);
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
} else {
Settings::values.rng_seed.SetValue(0);
}
Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled);
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
if (custom_rtc_enabled) {
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
} else {
Settings::values.custom_rtc = 0;
}
Settings::values.custom_rtc_enabled = custom_rtc_enabled;
ReadSetting("System", Settings::values.language_index);
ReadSetting("System", Settings::values.region_index);
ReadSetting("System", Settings::values.time_zone_index);
ReadSetting("System", Settings::values.sound_index);
// Core
ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.memory_layout_mode);
// Cpu
ReadSetting("Cpu", Settings::values.cpu_accuracy);
ReadSetting("Cpu", Settings::values.cpu_debug_mode);
ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
// Renderer
ReadSetting("Renderer", Settings::values.renderer_backend);
ReadSetting("Renderer", Settings::values.renderer_debug);
ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
ReadSetting("Renderer", Settings::values.vulkan_device);
ReadSetting("Renderer", Settings::values.resolution_setup);
ReadSetting("Renderer", Settings::values.scaling_filter);
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
ReadSetting("Renderer", Settings::values.anti_aliasing);
ReadSetting("Renderer", Settings::values.fullscreen_mode);
ReadSetting("Renderer", Settings::values.aspect_ratio);
ReadSetting("Renderer", Settings::values.max_anisotropy);
ReadSetting("Renderer", Settings::values.use_speed_limit);
ReadSetting("Renderer", Settings::values.speed_limit);
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
ReadSetting("Renderer", Settings::values.vsync_mode);
ReadSetting("Renderer", Settings::values.shader_backend);
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
ReadSetting("Renderer", Settings::values.nvdec_emulation);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
// Use GPU accuracy normal by default on Android
Settings::values.gpu_accuracy = static_cast<Settings::GpuAccuracy>(config->GetInteger(
"Renderer", "gpu_accuracy", static_cast<u32>(Settings::GpuAccuracy::Normal)));
// Use GPU default anisotropic filtering on Android
Settings::values.max_anisotropy =
static_cast<Settings::AnisotropyMode>(config->GetInteger("Renderer", "max_anisotropy", 1));
// Disable ASTC compute by default on Android
Settings::values.accelerate_astc.SetValue(
config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu
: Settings::AstcDecodeMode::Cpu);
// Enable asynchronous presentation by default on Android
Settings::values.async_presentation =
config->GetBoolean("Renderer", "async_presentation", true);
// Disable force_max_clock by default on Android
Settings::values.renderer_force_max_clock =
config->GetBoolean("Renderer", "force_max_clock", false);
// Disable use_reactive_flushing by default on Android
Settings::values.use_reactive_flushing =
config->GetBoolean("Renderer", "use_reactive_flushing", false);
// Audio
ReadSetting("Audio", Settings::values.sink_id);
ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume);
// Miscellaneous
// log_filter has a different default here than from common
Settings::values.log_filter = "*:Info";
ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
// Debugging
Settings::values.record_frame_times =
config->GetBoolean("Debugging", "record_frame_times", false);
ReadSetting("Debugging", Settings::values.dump_exefs);
ReadSetting("Debugging", Settings::values.dump_nso);
ReadSetting("Debugging", Settings::values.enable_fs_access_log);
ReadSetting("Debugging", Settings::values.reporting_services);
ReadSetting("Debugging", Settings::values.quest_flag);
ReadSetting("Debugging", Settings::values.use_debug_asserts);
ReadSetting("Debugging", Settings::values.use_auto_stub);
ReadSetting("Debugging", Settings::values.disable_macro_jit);
ReadSetting("Debugging", Settings::values.disable_macro_hle);
ReadSetting("Debugging", Settings::values.use_gdbstub);
ReadSetting("Debugging", Settings::values.gdbstub_port);
const auto title_list = config->Get("AddOns", "title_ids", "");
std::stringstream ss(title_list);
std::string line;
while (std::getline(ss, line, '|')) {
const auto title_id = std::strtoul(line.c_str(), nullptr, 16);
const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
std::stringstream inner_ss(disabled_list);
std::string inner_line;
std::vector<std::string> out;
while (std::getline(inner_ss, inner_line, '|')) {
out.push_back(inner_line);
}
Settings::values.disabled_addons.insert_or_assign(title_id, out);
}
// Web Service
ReadSetting("WebService", Settings::values.enable_telemetry);
ReadSetting("WebService", Settings::values.web_api_url);
ReadSetting("WebService", Settings::values.yuzu_username);
ReadSetting("WebService", Settings::values.yuzu_token);
// Network
ReadSetting("Network", Settings::values.network_interface);
// Android
ReadSetting("Android", AndroidSettings::values.picture_in_picture);
ReadSetting("Android", AndroidSettings::values.screen_layout);
}
void Config::Initialize(const std::string& config_name) {
const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
const auto config_file = fmt::format("{}.ini", config_name);
switch (type) {
case ConfigType::GlobalConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
break;
case ConfigType::PerGameConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
break;
case ConfigType::InputProfile:
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
LoadINI(DefaultINI::android_config_file);
return;
}
LoadINI(DefaultINI::android_config_file);
ReadValues();
}

View File

@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include "common/settings.h"
class INIReader;
class Config {
bool LoadINI(const std::string& default_contents = "", bool retry = true);
public:
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Initialize(const std::string& config_name);
private:
/**
* Applies a value read from the config to a Setting.
*
* @param group The name of the INI group
* @param setting The yuzu setting to modify
*/
template <typename Type, bool ranged>
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
void ReadValues();
const ConfigType type;
std::unique_ptr<INIReader> config;
std::string config_loc;
const bool global;
};

View File

@ -1,511 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace DefaultINI {
const char* android_config_file = R"(
[ControlsP0]
# The input devices and parameters for each Switch native input
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# Indicates if this player should be connected at boot
connected=
# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "button"(optional): the index of the button to bind
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
# triggered if the axis value crosses
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
# is greater than the threshold; "-" means the button is triggered when the axis value
# is smaller than the threshold
button_a=
button_b=
button_x=
button_y=
button_lstick=
button_rstick=
button_l=
button_r=
button_zl=
button_zr=
button_plus=
button_minus=
button_dleft=
button_dup=
button_dright=
button_ddown=
button_lstick_left=
button_lstick_up=
button_lstick_right=
button_lstick_down=
button_sl=
button_sr=
button_home=
button_screenshot=
# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
# - "up", "down", "left", "right": sub-devices for each direction.
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
# - "modifier": sub-devices as a modifier.
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
# Must be in range of 0.0-1.0. Defaults to 0.5
# - "sdl" for joystick input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
lstick=
rstick=
# for motion input, the following devices are available:
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for motion input using SDL. Required parameters:
# - "guid": SDL identification GUID of the joystick
# - "port": the index of the joystick to bind
# - "motion": the index of the motion sensor to bind
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
# - "port": the port of the cemu hook server
# - "pad": the index of the joystick
# - "motion": the index of the motion sensor of the joystick to bind
motionleft=
motionright=
[ControlsGeneral]
# To use the debug_pad, prepend `debug_pad_` before each button setting above.
# i.e. debug_pad_button_a=
# Enable debug pad inputs to the guest
# 0 (default): Disabled, 1: Enabled
debug_pad_enabled =
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
# Whether to enable or disable accurate vibrations
# 0 (default): Disabled, 1: Enabled
enable_accurate_vibrations=
# Enables controller motion inputs
# 0: Disabled, 1 (default): Enabled
motion_enabled =
# Defines the udp device's touch screen coordinate system for cemuhookudp devices
# - "min_x", "min_y", "max_x", "max_y"
touch_device=
# for mapping buttons to touch inputs.
#touch_from_button_map=1
#touch_from_button_maps_0_name=default
#touch_from_button_maps_0_count=2
#touch_from_button_maps_0_bind_0=foo
#touch_from_button_maps_0_bind_1=bar
# etc.
# List of Cemuhook UDP servers, delimited by ','.
# Default: 127.0.0.1:26760
# Example: 127.0.0.1:26760,123.4.5.67:26761
udp_input_servers =
# Enable controlling an axis via a mouse input.
# 0 (default): Off, 1: On
mouse_panning =
# Set mouse sensitivity.
# Default: 1.0
mouse_panning_sensitivity =
# Emulate an analog control stick from keyboard inputs.
# 0 (default): Disabled, 1: Enabled
emulate_analog_keyboard =
# Enable mouse inputs to the guest
# 0 (default): Disabled, 1: Enabled
mouse_enabled =
# Enable keyboard inputs to the guest
# 0 (default): Disabled, 1: Enabled
keyboard_enabled =
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
use_multi_core =
# Enable unsafe extended guest system memory layout (8GB DRAM)
# 0 (default): Disabled, 1: Enabled
use_unsafe_extended_memory_layout =
[Cpu]
# Adjusts various optimizations.
# Auto-select mode enables choice unsafe optimizations.
# Accurate enables only safe optimizations.
# Unsafe allows any unsafe optimizations.
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
cpu_accuracy =
# Allow disabling safe optimizations.
# 0 (default): Disabled, 1: Enabled
cpu_debug_mode =
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
# Enable Host MMU Emulation (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem =
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_fastmem_exclusives =
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_recompile_exclusives =
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_ignore_memory_aborts =
# Enable unfuse FMA (improve performance on CPUs without FMA)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_unfuse_fma =
# Enable faster FRSQRTE and FRECPE
# Only enabled if cpu_accuracy is set to Unsafe.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_reduce_fp_error =
# Enable faster ASIMD instructions (32 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_standard_fpcr =
# Enable inaccurate NaN handling
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_inaccurate_nan =
# Disable address space checks (64 bits only)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_fastmem_check =
# Enable faster exclusive instructions
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_global_monitor =
[Renderer]
# Which backend API to use.
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
backend =
# Whether to enable asynchronous presentation (Vulkan only)
# 0: Off, 1 (default): On
async_presentation =
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
# 0 (default): Disabled, 1: Enabled
force_max_clock =
# Enable graphics API debugging mode.
# 0 (default): Disabled, 1: Enabled
debug =
# Enable shader feedback.
# 0 (default): Disabled, 1: Enabled
renderer_shader_feedback =
# Enable Nsight Aftermath crash dumps
# 0 (default): Disabled, 1: Enabled
nsight_aftermath =
# Disable shader loop safety checks, executing the shader without loop logic changes
# 0 (default): Disabled, 1: Enabled
disable_shader_loop_safety_checks =
# Which Vulkan physical device to use (defaults to 0)
vulkan_device =
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
# 2 (default): 1x (720p/1080p)
# 3: 2x (1440p/2160p)
# 4: 3x (2160p/3240p)
# 5: 4x (2880p/4320p)
# 6: 5x (3600p/5400p)
# 7: 6x (4320p/6480p)
resolution_setup =
# Pixel filter to use when up- or down-sampling rendered frames.
# 0: Nearest Neighbor
# 1 (default): Bilinear
# 2: Bicubic
# 3: Gaussian
# 4: ScaleForce
# 5: AMD FidelityFX™ Super Resolution [Vulkan Only]
scaling_filter =
# Anti-Aliasing (AA)
# 0 (default): None, 1: FXAA
anti_aliasing =
# Whether to use fullscreen or borderless window mode
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
fullscreen_mode =
# Aspect ratio
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window
aspect_ratio =
# Anisotropic filtering
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
max_anisotropy =
# Whether to enable VSync or not.
# OpenGL: Values other than 0 enable VSync
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed
use_vsync =
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
# not available and GLASM is selected, GLSL will be used.
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
shader_backend =
# Whether to allow asynchronous shader building.
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
# 0 (default): Off, 1: On
use_reactive_flushing =
# NVDEC emulation.
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
nvdec_emulation =
# Accelerate ASTC texture decoding.
# 0 (default): Off, 1: On
accelerate_astc =
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
# 0: Off, 1: On (default)
use_speed_limit =
# Limits the speed of the game to run no faster than this value as a percentage of target speed
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
speed_limit =
# Whether to use disk based shader cache
# 0: Off, 1 (default): On
use_disk_shader_cache =
# Which gpu accuracy level to use
# 0 (default): Normal, 1: High, 2: Extreme (Very slow)
gpu_accuracy =
# Whether to use asynchronous GPU emulation
# 0 : Off (slow), 1 (default): On (fast)
use_asynchronous_gpu_emulation =
# Inform the guest that GPU operations completed more quickly than they did.
# 0: Off, 1 (default): On
use_fast_gpu_time =
# Force unmodified buffers to be flushed, which can cost performance.
# 0: Off (default), 1: On
use_pessimistic_flushes =
# Whether to use garbage collection or not for GPU caches.
# 0 (default): Off, 1: On
use_caches_gc =
# The clear color for the renderer. What shows up on the sides of the bottom screen.
# Must be in range of 0-255. Defaults to 0 for all.
bg_red =
bg_blue =
bg_green =
[Audio]
# Which audio output engine to use.
# auto (default): Auto-select
# cubeb: Cubeb audio engine (if available)
# sdl2: SDL2 audio engine (if available)
# null: No audio output
output_engine =
# Which audio device to use.
# auto (default): Auto-select
output_device =
# Output volume.
# 100 (default): 100%, 0; mute
volume =
[Data Storage]
# Whether to create a virtual SD card.
# 1: Yes, 0 (default): No
use_virtual_sd =
# Whether or not to enable gamecard emulation
# 1: Yes, 0 (default): No
gamecard_inserted =
# Whether or not the gamecard should be emulated as the current game
# If 'gamecard_inserted' is 0 this setting is irrelevant
# 1: Yes, 0 (default): No
gamecard_current_game =
# Path to an XCI file to use as the gamecard
# If 'gamecard_inserted' is 0 this setting is irrelevant
# If 'gamecard_current_game' is 1 this setting is irrelevant
gamecard_path =
[System]
# Whether the system is docked
# 1 (default): Yes, 0: No
use_docked_mode =
# Sets the seed for the RNG generator built into the switch
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
rng_seed_enabled =
rng_seed =
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
# This will auto-increment, with the time set being the time the game is started
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
custom_rtc_enabled =
custom_rtc =
# Sets the systems language index
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
language_index =
# The system region that yuzu will use during emulation
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_index =
# The system time zone that yuzu will use during emulation
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
time_zone_index =
# Sets the sound output mode.
# 0: Mono, 1 (default): Stereo, 2: Surround
sound_index =
[Miscellaneous]
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Trace
# Use developer keys
# 0 (default): Disabled, 1: Enabled
use_dev_keys =
[Debugging]
# Record frame time data, can be found in the log directory. Boolean value
record_frame_times =
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
dump_exefs=false
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
dump_nso=false
# Determines whether or not yuzu will save the filesystem access log.
enable_fs_access_log=false
# Enables verbose reporting services
reporting_services =
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
# false: Disabled (default), true: Enabled
use_debug_asserts =
# Determines whether unimplemented HLE service calls should be automatically stubbed.
# false: Disabled (default), true: Enabled
use_auto_stub =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false
# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
# false: Disabled (default), true: Enabled
use_gdbstub=false
# The port to use for the GDB server, if it is enabled.
gdbstub_port=6543
[WebService]
# Whether or not to enable telemetry
# 0: No, 1 (default): Yes
enable_telemetry =
# URL for Web API
web_api_url = https://api.yuzu-emu.org
# Username and token for yuzu Web Service
# See https://profile.yuzu-emu.org/ for more info
yuzu_username =
yuzu_token =
[Network]
# Name of the network interface device to use with yuzu LAN play.
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
# e.g. On Windows: 'Ethernet', 'Wi-Fi'
network_interface =
[AddOns]
# Used to disable add-ons
# List of title IDs of games that will have add-ons disabled (separated by '|'):
title_ids =
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
)";
} // namespace DefaultINI

View File

@ -9,6 +9,7 @@
#include "input_common/drivers/virtual_gamepad.h" #include "input_common/drivers/virtual_gamepad.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/native.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
m_window_width = ANativeWindow_getWidth(surface); m_window_width = ANativeWindow_getWidth(surface);
@ -57,6 +58,13 @@ void EmuWindow_Android::OnRemoveNfcTag() {
m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo(); m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
} }
void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) {
EmulationSession::GetInstance().OnEmulationStarted();
m_first_frame = true;
}
}
EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem,
ANativeWindow* surface, ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library) std::shared_ptr<Common::DynamicLibrary> driver_library)

View File

@ -45,7 +45,7 @@ public:
float gyro_z, float accel_x, float accel_y, float accel_z); float gyro_z, float accel_x, float accel_y, float accel_z);
void OnReadNfcTag(std::span<u8> data); void OnReadNfcTag(std::span<u8> data);
void OnRemoveNfcTag(); void OnRemoveNfcTag();
void OnFrameDisplayed() override {} void OnFrameDisplayed() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@ -61,4 +61,6 @@ private:
float m_window_height{}; float m_window_height{};
std::shared_ptr<Common::DynamicLibrary> m_driver_library; std::shared_ptr<Common::DynamicLibrary> m_driver_library;
bool m_first_frame = false;
}; };

View File

@ -52,8 +52,8 @@
#include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/config.h"
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -372,8 +372,6 @@ void EmulationSession::RunEmulation() {
m_system.InitializeDebugger(); m_system.InitializeDebugger();
} }
OnEmulationStarted();
while (true) { while (true) {
{ {
[[maybe_unused]] std::unique_lock lock(m_mutex); [[maybe_unused]] std::unique_lock lock(m_mutex);
@ -666,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
jboolean reload) { jboolean reload) {
// Create the default config.ini.
Config{};
// Initialize the emulated system. // Initialize the emulated system.
if (!reload) { if (!reload) {
EmulationSession::GetInstance().System().Initialize(); EmulationSession::GetInstance().System().Initialize();
@ -682,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4); jdoubleArray j_stats = env->NewDoubleArray(4);

View File

@ -52,9 +52,10 @@ public:
void OnGamepadDisconnectEvent([[maybe_unused]] int index); void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted();
private: private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStarted();
static void OnEmulationStopped(Core::SystemResultStatus result); static void OnEmulationStopped(Core::SystemResultStatus result);
private: private:

View File

@ -5,11 +5,14 @@
#include <jni.h> #include <jni.h>
#include "android_config.h"
#include "android_settings.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "frontend_common/config.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/config.h"
#include "uisettings.h" std::unique_ptr<AndroidConfig> config;
template <typename T> template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
config = std::make_unique<AndroidConfig>();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
config.reset();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::ReloadAllValues();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
config->AndroidConfig::SaveAllValues();
}
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
jstring jkey, jboolean getDefault) { jstring jkey, jboolean getDefault) {
auto setting = getSetting<bool>(env, jkey); auto setting = getSetting<bool>(env, jkey);

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/>
</vector>

View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/about" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/image_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:padding="20dp"
android:src="@drawable/ic_yuzu_title" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/about"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/about_app_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_contributors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/contributors"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/contributors_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/licenses"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:text="@string/licenses_description"
android:textAlignment="viewStart" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_build_hash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="@string/build"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
tools:text="abc123" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/button_discord"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_discord"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_website"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_website"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
<Button
android:id="@+id/button_github"
style="?attr/materialIconButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:icon="@drawable/ic_github"
app:iconGravity="textEnd"
app:iconSize="24dp"
app:iconTint="?attr/colorOnSurface" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -6,8 +6,8 @@
android:id="@+id/option_card" android:id="@+id/option_card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="12dp" android:layout_marginBottom="24dp"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="12dp"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:backgroundTint="?attr/colorSurfaceVariant" android:backgroundTint="?attr/colorSurfaceVariant"
android:clickable="true" android:clickable="true"

View File

@ -38,17 +38,17 @@
<ImageView <ImageView
android:id="@+id/image_logo" android:id="@+id/image_logo"
android:layout_width="250dp" android:layout_width="150dp"
android:layout_height="250dp" android:layout_height="150dp"
android:layout_marginTop="20dp" android:layout_marginTop="24dp"
android:layout_marginBottom="28dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_title" /> android:src="@drawable/ic_yuzu_title" />
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp" />
android:layout_marginTop="28dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -139,7 +139,7 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.BodyMedium" style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="left" android:layout_gravity="left"
@ -147,7 +147,8 @@
android:focusable="false" android:focusable="false"
android:paddingHorizontal="20dp" android:paddingHorizontal="20dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" android:shadowColor="@android:color/black"
android:shadowRadius="3"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</FrameLayout> </FrameLayout>

View File

@ -14,13 +14,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface"
android:paddingHorizontal="8dp">
<ImageView <ImageView
android:id="@+id/logo_image" android:id="@+id/logo_image"
android:layout_width="128dp" android:layout_width="96dp"
android:layout_height="128dp" android:layout_height="96dp"
android:layout_margin="64dp" android:layout_marginVertical="32dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_full" /> android:src="@drawable/ic_yuzu_full" />

View File

@ -10,41 +10,59 @@
android:focusable="true" android:focusable="true"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="72dp" android:minHeight="72dp"
android:padding="@dimen/spacing_large"> android:padding="16dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView <ImageView
android:id="@+id/text_setting_name" android:id="@+id/icon"
style="@style/TextAppearance.Material3.HeadlineMedium" android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
app:tint="?attr/colorOnSurface" />
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:orientation="vertical">
android:textSize="16sp"
app:lineHeight="22dp"
tools:text="Setting Name" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_description" android:id="@+id/text_setting_name"
style="@style/TextAppearance.Material3.BodySmall" style="@style/TextAppearance.Material3.HeadlineMedium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small" android:textAlignment="viewStart"
android:textAlignment="viewStart" android:textSize="17sp"
tools:text="@string/app_disclaimer" /> app:lineHeight="22dp"
tools:text="Setting Name" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value" android:id="@+id/text_setting_description"
style="@style/TextAppearance.Material3.LabelMedium" style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small" android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textStyle="bold" tools:text="@string/app_disclaimer" />
tools:text="1x" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_setting_value"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:textAlignment="viewStart"
android:textStyle="bold"
android:textSize="13sp"
tools:text="1x" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -8,9 +8,7 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minHeight="72dp" android:minHeight="72dp"
android:paddingVertical="@dimen/spacing_large" android:padding="16dp">
android:paddingStart="@dimen/spacing_large"
android:paddingEnd="24dp">
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_widget" android:id="@+id/switch_widget"
@ -24,7 +22,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large" android:layout_marginEnd="24dp"
android:layout_toStartOf="@+id/switch_widget" android:layout_toStartOf="@+id/switch_widget"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="vertical"> android:orientation="vertical">
@ -35,7 +33,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="16sp" android:textSize="17sp"
app:lineHeight="28dp" app:lineHeight="28dp"
tools:text="@string/frame_limit_enable" /> tools:text="@string/frame_limit_enable" />

View File

@ -7,7 +7,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical" android:layout_gravity="start|center_vertical"
android:paddingHorizontal="@dimen/spacing_large" android:paddingHorizontal="@dimen/spacing_large"
android:paddingVertical="16dp" android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="?attr/colorPrimary" android:textColor="?attr/colorPrimary"
android:textStyle="bold" android:textStyle="bold"

View File

@ -2,7 +2,6 @@
<resources> <resources>
<string-array name="regionNames"> <string-array name="regionNames">
<item>@string/auto</item>
<item>@string/region_australia</item> <item>@string/region_australia</item>
<item>@string/region_china</item> <item>@string/region_china</item>
<item>@string/region_europe</item> <item>@string/region_europe</item>
@ -13,7 +12,6 @@
</string-array> </string-array>
<integer-array name="regionValues"> <integer-array name="regionValues">
<item>-1</item>
<item>3</item> <item>3</item>
<item>4</item> <item>4</item>
<item>2</item> <item>2</item>

View File

@ -240,6 +240,7 @@
<string name="shutting_down">Shutting down…</string> <string name="shutting_down">Shutting down…</string>
<string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
<string name="reset_to_default">Reset to default</string> <string name="reset_to_default">Reset to default</string>
<string name="reset_to_default_description">Resets all advanced settings</string>
<string name="reset_all_settings">Reset all settings?</string> <string name="reset_all_settings">Reset all settings?</string>
<string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
<string name="settings_reset">Settings reset</string> <string name="settings_reset">Settings reset</string>
@ -271,10 +272,14 @@
<string name="preferences_settings">Settings</string> <string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string> <string name="preferences_general">General</string>
<string name="preferences_system">System</string> <string name="preferences_system">System</string>
<string name="preferences_system_description">Docked mode, region, language</string>
<string name="preferences_graphics">Graphics</string> <string name="preferences_graphics">Graphics</string>
<string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
<string name="preferences_audio">Audio</string> <string name="preferences_audio">Audio</string>
<string name="preferences_audio_description">Output engine, volume</string>
<string name="preferences_theme">Theme and color</string> <string name="preferences_theme">Theme and color</string>
<string name="preferences_debug">Debug</string> <string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
<!-- ROM loading errors --> <!-- ROM loading errors -->
<string name="loader_error_encrypted">Your ROM is encrypted</string> <string name="loader_error_encrypted">Your ROM is encrypted</string>

View File

@ -146,7 +146,7 @@ public:
return; return;
} }
paused = true; SignalPause();
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
} }

View File

@ -111,7 +111,7 @@ public:
if (device == 0 || paused) { if (device == 0 || paused) {
return; return;
} }
paused = true; SignalPause();
SDL_PauseAudioDevice(device, 1); SDL_PauseAudioDevice(device, 1);
} }

View File

@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
void SinkStream::WaitFreeSpace(std::stop_token stop_token) { void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
std::unique_lock lk{release_mutex}; std::unique_lock lk{release_mutex};
release_cv.wait_for(lk, std::chrono::milliseconds(5), release_cv.wait_for(lk, std::chrono::milliseconds(5),
[this]() { return queued_buffers < max_queue_size; }); [this]() { return paused || queued_buffers < max_queue_size; });
if (queued_buffers > max_queue_size + 3) { if (queued_buffers > max_queue_size + 3) {
Common::CondvarWait(release_cv, lk, stop_token, Common::CondvarWait(release_cv, lk, stop_token,
[this] { return queued_buffers < max_queue_size; }); [this] { return paused || queued_buffers < max_queue_size; });
} }
} }
void SinkStream::SignalPause() {
{
std::scoped_lock lk{release_mutex};
paused = true;
}
release_cv.notify_one();
}
} // namespace AudioCore::Sink } // namespace AudioCore::Sink

View File

@ -213,6 +213,12 @@ public:
*/ */
void WaitFreeSpace(std::stop_token stop_token); void WaitFreeSpace(std::stop_token stop_token);
protected:
/**
* Unblocks the ADSP if the stream is paused.
*/
void SignalPause();
protected: protected:
/// Core system /// Core system
Core::System& system; Core::System& system;

View File

@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
case Category::Ui: case Category::Ui:
case Category::UiGeneral: case Category::UiGeneral:
return "UI"; return "UI";
case Category::UiAudio:
return "UiAudio";
case Category::UiLayout: case Category::UiLayout:
return "UiLayout"; return "UILayout";
case Category::UiGameList: case Category::UiGameList:
return "UiGameList"; return "UIGameList";
case Category::Screenshots: case Category::Screenshots:
return "Screenshots"; return "Screenshots";
case Category::Shortcuts: case Category::Shortcuts:

View File

@ -153,7 +153,7 @@ struct Values {
true, true,
true}; true};
Setting<bool, false> audio_muted{ Setting<bool, false> audio_muted{
linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
Setting<bool, false> dump_audio_commands{ Setting<bool, false> dump_audio_commands{
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
@ -232,7 +232,11 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_gpu_emulation{ SwitchableSetting<bool> use_asynchronous_gpu_emulation{
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,
#else
AstcDecodeMode::Gpu, AstcDecodeMode::Gpu,
#endif
AstcDecodeMode::Cpu, AstcDecodeMode::Cpu,
AstcDecodeMode::CpuAsynchronous, AstcDecodeMode::CpuAsynchronous,
"accelerate_astc", "accelerate_astc",
@ -304,7 +308,11 @@ struct Values {
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
#ifdef ANDROID
GpuAccuracy::Normal,
#else
GpuAccuracy::High, GpuAccuracy::High,
#endif
GpuAccuracy::Normal, GpuAccuracy::Normal,
GpuAccuracy::Extreme, GpuAccuracy::Extreme,
"gpu_accuracy", "gpu_accuracy",
@ -313,20 +321,38 @@ struct Values {
true, true,
true}; true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{ SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, #ifdef ANDROID
"max_anisotropy", Category::RendererAdvanced}; AnisotropyMode::Default,
#else
AnisotropyMode::Automatic,
#endif
AnisotropyMode::Automatic,
AnisotropyMode::X16,
"max_anisotropy",
Category::RendererAdvanced};
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed,
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed,
AstcRecompression::Bc3, AstcRecompression::Bc3,
"astc_recompression", "astc_recompression",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", SwitchableSetting<bool> async_presentation{linkage,
Category::RendererAdvanced}; #ifdef ANDROID
true,
#else
false,
#endif
"async_presentation", Category::RendererAdvanced};
SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", SwitchableSetting<bool> use_reactive_flushing{linkage,
#ifdef ANDROID
false,
#else
true,
#endif
"use_reactive_flushing",
Category::RendererAdvanced}; Category::RendererAdvanced};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererAdvanced}; Category::RendererAdvanced};
@ -358,6 +384,8 @@ struct Values {
Category::RendererDebug}; Category::RendererDebug};
// TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control
bool renderer_amdvlk_depth_bias_workaround{}; bool renderer_amdvlk_depth_bias_workaround{};
Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder",
Category::RendererDebug};
// System // System
SwitchableSetting<Language, true> language_index{linkage, SwitchableSetting<Language, true> language_index{linkage,
@ -390,7 +418,11 @@ struct Values {
Setting<s32> current_user{linkage, 0, "current_user", Category::System}; Setting<s32> current_user{linkage, 0, "current_user", Category::System};
SwitchableSetting<ConsoleMode> use_docked_mode{linkage, SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
#ifdef ANDROID
ConsoleMode::Handheld,
#else
ConsoleMode::Docked, ConsoleMode::Docked,
#endif
"use_docked_mode", "use_docked_mode",
Category::System, Category::System,
Specialization::Radio, Specialization::Radio,

View File

@ -32,6 +32,7 @@ enum class Category : u32 {
AddOns, AddOns,
Controls, Controls,
Ui, Ui,
UiAudio,
UiGeneral, UiGeneral,
UiLayout, UiLayout,
UiGameList, UiGameList,

View File

@ -153,6 +153,14 @@ void ARM_Interface::Run() {
Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
HaltReason hr{}; HaltReason hr{};
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a step was performed // Notify the debugger and go to sleep if a step was performed
// and this thread has been scheduled again. // and this thread has been scheduled again.
if (current_thread->GetStepState() == StepState::StepPerformed) { if (current_thread->GetStepState() == StepState::StepPerformed) {
@ -174,14 +182,6 @@ void ARM_Interface::Run() {
} }
system.ExitCPUProfile(); system.ExitCPUProfile();
// If the thread is scheduled for termination, exit the thread.
if (current_thread->HasDpc()) {
if (current_thread->IsTerminationRequested()) {
current_thread->Exit();
UNREACHABLE();
}
}
// Notify the debugger and go to sleep if a breakpoint was hit, // Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason. // or if the thread is unable to continue for any reason.
if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {

View File

@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
} }
void CoreTiming::ClearPendingEvents() { void CoreTiming::ClearPendingEvents() {
std::scoped_lock lock{basic_lock};
event_queue.clear(); event_queue.clear();
} }
@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
} }
bool CoreTiming::HasPendingEvents() const { bool CoreTiming::HasPendingEvents() const {
std::scoped_lock lock{basic_lock};
return !(wait_set && event_queue.empty()); return !(wait_set && event_queue.empty());
} }

View File

@ -161,7 +161,7 @@ private:
std::shared_ptr<EventType> ev_lost; std::shared_ptr<EventType> ev_lost;
Common::Event event{}; Common::Event event{};
Common::Event pause_event{}; Common::Event pause_event{};
std::mutex basic_lock; mutable std::mutex basic_lock;
std::mutex advance_lock; std::mutex advance_lock;
std::unique_ptr<std::jthread> timer_thread; std::unique_ptr<std::jthread> timer_thread;
std::atomic<bool> paused{}; std::atomic<bool> paused{};

View File

@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
} }
} }
static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
Kernel::KMemoryInfo mem_info;
Kernel::Svc::MemoryInfo svc_mem_info;
Kernel::Svc::PageInfo page_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
return cur_addr - 1;
}
static Loader::AppLoader::Modules FindModules(Core::System& system) {
Loader::AppLoader::Modules modules;
auto& page_table = system.ApplicationProcess()->GetPageTable();
auto& memory = system.ApplicationMemory();
VAddr cur_addr = 0;
// Look for executable sections in Code or AliasCode regions.
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();
if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute &&
(svc_mem_info.state == Kernel::Svc::MemoryState::Code ||
svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) {
// Try to read the module name from its path.
constexpr s32 PathLengthMax = 0x200;
struct {
u32 zero;
s32 path_length;
std::array<char, PathLengthMax> path;
} module_path;
if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path,
sizeof(module_path))) {
if (module_path.zero == 0 && module_path.path_length > 0) {
// Truncate module name.
module_path.path[PathLengthMax - 1] = '\0';
// Ignore leading directories.
char* path_pointer = module_path.path.data();
for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
module_path.path[i] != '\0';
i++) {
if (module_path.path[i] == '/' || module_path.path[i] == '\\') {
path_pointer = module_path.path.data() + i + 1;
}
}
// Insert output.
modules.emplace(svc_mem_info.base_address, path_pointer);
}
}
}
// Check if we're done.
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) {
break;
}
cur_addr = next_address;
}
return modules;
}
static VAddr FindMainModuleEntrypoint(Core::System& system) {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
// Do we have a module named main?
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
return main->first;
}
// Do we have any loaded executable sections?
modules = FindModules(system);
if (!modules.empty()) {
return modules.begin()->first;
}
// As a last resort, use the start of the code region.
return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart());
}
void GDBStub::HandleQuery(std::string_view command) { void GDBStub::HandleQuery(std::string_view command) {
if (command.starts_with("TStatus")) { if (command.starts_with("TStatus")) {
// no tracepoint support // no tracepoint support
@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto target_xml{arch->GetTargetXML()}; const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30))); SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) { } else if (command.starts_with("Offsets")) {
Loader::AppLoader::Modules modules; const auto main_offset = FindMainModuleEntrypoint(system);
system.GetAppLoader().ReadNSOModules(modules); SendReply(fmt::format("TextSeg={:x}", main_offset));
const auto main = std::find_if(modules.begin(), modules.end(),
[](const auto& key) { return key.second == "main"; });
if (main != modules.end()) {
SendReply(fmt::format("TextSeg={:x}", main->first));
} else {
SendReply(fmt::format(
"TextSeg={:x}",
GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart())));
}
} else if (command.starts_with("Xfer:libraries:read::")) { } else if (command.starts_with("Xfer:libraries:read::")) {
Loader::AppLoader::Modules modules; auto modules = FindModules(system);
system.GetAppLoader().ReadNSOModules(modules);
std::string buffer; std::string buffer;
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
@ -727,37 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory
} }
} }
static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) {
Kernel::KMemoryInfo mem_info;
Kernel::Svc::MemoryInfo svc_mem_info;
Kernel::Svc::PageInfo page_info;
VAddr cur_addr{base};
// Expect: r-x Code (.text)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
return cur_addr - 1;
}
// Expect: r-- Code (.rodata)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
return cur_addr - 1;
}
// Expect: rw- CodeData (.data)
R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
svc_mem_info = mem_info.GetSvcMemoryInfo();
cur_addr = svc_mem_info.base_address + svc_mem_info.size;
return cur_addr - 1;
}
void GDBStub::HandleRcmd(const std::vector<u8>& command) { void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply; std::string reply;
@ -784,8 +856,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n"; reply = "Fastmem is not enabled.\n";
} }
} else if (command_str == "get info") { } else if (command_str == "get info") {
Loader::AppLoader::Modules modules; auto modules = FindModules(system);
system.GetAppLoader().ReadNSOModules(modules);
reply = fmt::format("Process: {:#x} ({})\n" reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n", "Program Id: {:#018x}\n",

View File

@ -5,6 +5,7 @@
#include "core/hle/kernel/k_capabilities.h" #include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_process_page_table.h" #include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h" #include "core/hle/kernel/svc_version.h"
@ -329,6 +330,8 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTab
// Map the range. // Map the range.
R_TRY(this->MapRange_(cap, size_cap, page_table)); R_TRY(this->MapRange_(cap, size_cap, page_table));
} else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
continue;
} else { } else {
R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
} }

View File

@ -1617,9 +1617,9 @@ void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress a
const KMemoryInfo info = it->GetMemoryInfo(); const KMemoryInfo info = it->GetMemoryInfo();
// Determine the range to map. // Determine the range to map.
KProcessAddress map_address = std::max(info.GetAddress(), GetInteger(start_address)); KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address));
const KProcessAddress map_end_address = const KProcessAddress map_end_address =
std::min(info.GetEndAddress(), GetInteger(end_address)); std::min<u64>(info.GetEndAddress(), GetInteger(end_address));
ASSERT(map_end_address != map_address); ASSERT(map_end_address != map_address);
// Determine if we should disable head merge. // Determine if we should disable head merge.

View File

@ -138,7 +138,7 @@ void IRS::RunMomentProcessor(HLERequestContext& ctx) {
if (result.IsSuccess()) { if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<MomentProcessor>(parameters.camera_handle, device); MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config); image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,

View File

@ -3,16 +3,18 @@
#include <queue> #include <queue>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h" #include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h" #include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS { namespace Service::IRS {
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_, ClusteringProcessor::ClusteringProcessor(Core::System& system_,
Core::IrSensor::DeviceFormat& device_format, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index) std::size_t npad_index)
: device{device_format} { : device{device_format}, system{system_} {
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
@ -48,7 +50,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
} }
next_state = {}; next_state = {};
const auto camera_data = npad_device->GetCamera(); const auto& camera_data = npad_device->GetCamera();
auto filtered_image = camera_data.data; auto filtered_image = camera_data.data;
RemoveLowIntensityData(filtered_image); RemoveLowIntensityData(filtered_image);
@ -83,7 +85,7 @@ void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType ty
} }
next_state.sampling_number = camera_data.sample; next_state.sampling_number = camera_data.sample;
next_state.timestamp = next_state.timestamp + 131; next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->clustering_lifo.WriteNextEntry(next_state); shared_memory->clustering_lifo.WriteNextEntry(next_state);
@ -202,14 +204,14 @@ ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
} }
u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
if ((y * width) + x > data.size()) { if ((y * width) + x >= data.size()) {
return 0; return 0;
} }
return data[(y * width) + x]; return data[(y * width) + x];
} }
void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) { void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
if ((y * width) + x > data.size()) { if ((y * width) + x >= data.size()) {
return; return;
} }
data[(y * width) + x] = value; data[(y * width) + x] = value;

View File

@ -8,6 +8,10 @@
#include "core/hle/service/hid/irs_ring_lifo.h" #include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core {
class System;
}
namespace Core::HID { namespace Core::HID {
class EmulatedController; class EmulatedController;
} // namespace Core::HID } // namespace Core::HID
@ -15,8 +19,7 @@ class EmulatedController;
namespace Service::IRS { namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase { class ClusteringProcessor final : public ProcessorBase {
public: public:
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_, explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index); std::size_t npad_index);
~ClusteringProcessor() override; ~ClusteringProcessor() override;
@ -106,5 +109,7 @@ private:
Core::IrSensor::DeviceFormat& device; Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device; Core::HID::EmulatedController* npad_device;
int callback_key{}; int callback_key{};
Core::System& system;
}; };
} // namespace Service::IRS } // namespace Service::IRS

View File

@ -49,7 +49,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
return; return;
} }
const auto camera_data = npad_device->GetCamera(); const auto& camera_data = npad_device->GetCamera();
// This indicates how much ambient light is present // This indicates how much ambient light is present
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;

View File

@ -1,24 +1,137 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/moment_processor.h" #include "core/hle/service/hid/irsensor/moment_processor.h"
namespace Service::IRS { namespace Service::IRS {
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
: device(device_format) { static constexpr std::size_t ImageWidth = 40;
static constexpr std::size_t ImageHeight = 30;
MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index)
: device(device_format), system{system_} {
npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
shared_memory = std::construct_at(
reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data));
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
.is_npad_service = true,
};
callback_key = npad_device->SetCallback(engine_callback);
} }
MomentProcessor::~MomentProcessor() = default; MomentProcessor::~MomentProcessor() {
npad_device->DeleteCallback(callback_key);
};
void MomentProcessor::StartProcessor() {} void MomentProcessor::StartProcessor() {
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
}
void MomentProcessor::SuspendProcessor() {} void MomentProcessor::SuspendProcessor() {}
void MomentProcessor::StopProcessor() {} void MomentProcessor::StopProcessor() {}
void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
if (type != Core::HID::ControllerTriggerType::IrSensor) {
return;
}
next_state = {};
const auto& camera_data = npad_device->GetCamera();
const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width);
const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height);
const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
const std::size_t block_width = window_width / Columns;
const std::size_t block_height = window_height / Rows;
for (std::size_t row = 0; row < Rows; row++) {
for (std::size_t column = 0; column < Columns; column++) {
const size_t x_pos = (column * block_width) + window_start_x;
const size_t y_pos = (row * block_height) + window_start_y;
auto& statistic = next_state.statistic[column + (row * Columns)];
statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height);
}
}
next_state.sampling_number = camera_data.sample;
next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
shared_memory->moment_lifo.WriteNextEntry(next_state);
if (!IsProcessorActive()) {
StartProcessor();
}
}
u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
if ((y * ImageWidth) + x >= data.size()) {
return 0;
}
return data[(y * ImageWidth) + x];
}
MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data,
std::size_t start_x,
std::size_t start_y,
std::size_t width,
std::size_t height) const {
// The actual implementation is always 320x240
static constexpr std::size_t RealWidth = 320;
static constexpr std::size_t RealHeight = 240;
static constexpr std::size_t Threshold = 30;
MomentStatistic statistic{};
std::size_t active_points{};
// Sum all data points on the block that meet with the threshold
for (std::size_t y = 0; y < width; y++) {
for (std::size_t x = 0; x < height; x++) {
const size_t x_pos = x + start_x;
const size_t y_pos = y + start_y;
const auto pixel =
GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight);
if (pixel < Threshold) {
continue;
}
statistic.average_intensity += pixel;
statistic.centroid.x += static_cast<float>(x_pos);
statistic.centroid.y += static_cast<float>(y_pos);
active_points++;
}
}
// Return an empty field if no points were available
if (active_points == 0) {
return {};
}
// Finally calculate the actual centroid and average intensity
statistic.centroid.x /= static_cast<float>(active_points);
statistic.centroid.y /= static_cast<float>(active_points);
statistic.average_intensity /= static_cast<f32>(width * height);
return statistic;
}
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time; current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain; current_config.camera_config.gain = config.camera_config.gain;
@ -29,6 +142,8 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
current_config.preprocess = current_config.preprocess =
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
npad_device->SetCameraFormat(format);
} }
} // namespace Service::IRS } // namespace Service::IRS

View File

@ -6,12 +6,22 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hid/irs_types.h" #include "core/hid/irs_types.h"
#include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/hid/irsensor/processor_base.h"
namespace Core {
class System;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::IRS { namespace Service::IRS {
class MomentProcessor final : public ProcessorBase { class MomentProcessor final : public ProcessorBase {
public: public:
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index);
~MomentProcessor() override; ~MomentProcessor() override;
// Called when the processor is initialized // Called when the processor is initialized
@ -27,6 +37,9 @@ public:
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
private: private:
static constexpr std::size_t Columns = 8;
static constexpr std::size_t Rows = 6;
// This is nn::irsensor::MomentProcessorConfig // This is nn::irsensor::MomentProcessorConfig
struct MomentProcessorConfig { struct MomentProcessorConfig {
Core::IrSensor::CameraConfig camera_config; Core::IrSensor::CameraConfig camera_config;
@ -50,12 +63,29 @@ private:
u64 timestamp; u64 timestamp;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4); INSERT_PADDING_BYTES(4);
std::array<MomentStatistic, 0x30> stadistic; std::array<MomentStatistic, Columns * Rows> statistic;
}; };
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
struct MomentSharedMemory {
Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo;
};
static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size");
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x,
std::size_t start_y, std::size_t width, std::size_t height) const;
MomentSharedMemory* shared_memory = nullptr;
MomentProcessorState next_state{};
MomentProcessorConfig current_config{}; MomentProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device; Core::IrSensor::DeviceFormat& device;
Core::HID::EmulatedController* npad_device;
int callback_key{};
Core::System& system;
}; };
} // namespace Service::IRS } // namespace Service::IRS

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(frontend_common STATIC
config.cpp
config.h
)
create_target_directory_groups(frontend_common)
target_link_libraries(frontend_common PUBLIC core SimpleIni PRIVATE common Boost::headers)

View File

@ -0,0 +1,918 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "common/settings_common.h"
#include "common/settings_enums.h"
#include "config.h"
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "network/network.h"
#include <boost/algorithm/string/replace.hpp>
namespace FS = Common::FS;
Config::Config(const ConfigType config_type)
: type(config_type), global{config_type == ConfigType::GlobalConfig} {}
void Config::Initialize(const std::string& config_name) {
const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
const auto config_file = fmt::format("{}.ini", config_name);
switch (type) {
case ConfigType::GlobalConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
void(FS::CreateParentDir(config_loc));
SetUpIni();
Reload();
break;
case ConfigType::PerGameConfig:
config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
void(FS::CreateParentDir(config_loc));
SetUpIni();
Reload();
break;
case ConfigType::InputProfile:
config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
void(FS::CreateParentDir(config_loc));
SetUpIni();
break;
}
}
void Config::Initialize(const std::optional<std::string> config_path) {
const std::filesystem::path default_sdl_config_path =
FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini";
config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path));
void(FS::CreateParentDir(config_loc));
SetUpIni();
Reload();
}
void Config::WriteToIni() const {
if (const SI_Error rc = config->SaveFile(config_loc.c_str()); rc < 0) {
LOG_ERROR(Frontend, "Config file could not be saved!");
}
}
void Config::SetUpIni() {
config = std::make_unique<CSimpleIniA>();
config->SetUnicode(true);
config->SetSpaces(false);
config->LoadFile(config_loc.c_str());
}
bool Config::IsCustomConfig() const {
return type == ConfigType::PerGameConfig;
}
void Config::ReadPlayerValues(const std::size_t player_index) {
std::string player_prefix;
if (type != ConfigType::InputProfile) {
player_prefix.append("player_").append(ToString(player_index)).append("_");
}
auto& player = Settings::values.players.GetValue()[player_index];
if (IsCustomConfig()) {
const auto profile_name =
ReadStringSetting(std::string(player_prefix).append("profile_name"));
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true)[player_index];
return;
}
player.profile_name = profile_name;
}
if (player_prefix.empty() && Settings::IsConfiguringGlobal()) {
const auto controller = static_cast<Settings::ControllerType>(
ReadIntegerSetting(std::string(player_prefix).append("type"),
static_cast<u8>(Settings::ControllerType::ProController)));
if (controller == Settings::ControllerType::LeftJoycon ||
controller == Settings::ControllerType::RightJoycon) {
player.controller_type = controller;
}
} else {
std::string connected_key = player_prefix;
player.connected = ReadBooleanSetting(connected_key.append("connected"),
std::make_optional(player_index == 0));
player.controller_type = static_cast<Settings::ControllerType>(
ReadIntegerSetting(std::string(player_prefix).append("type"),
static_cast<u8>(Settings::ControllerType::ProController)));
player.vibration_enabled = ReadBooleanSetting(
std::string(player_prefix).append("vibration_enabled"), std::make_optional(true));
player.vibration_strength = static_cast<int>(
ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100));
player.body_color_left = static_cast<u32>(ReadIntegerSetting(
std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE));
player.body_color_right = static_cast<u32>(ReadIntegerSetting(
std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED));
player.button_color_left = static_cast<u32>(
ReadIntegerSetting(std::string(player_prefix).append("button_color_left"),
Settings::JOYCON_BUTTONS_NEON_BLUE));
player.button_color_right = static_cast<u32>(
ReadIntegerSetting(std::string(player_prefix).append("button_color_right"),
Settings::JOYCON_BUTTONS_NEON_RED));
}
}
void Config::ReadTouchscreenValues() {
Settings::values.touchscreen.enabled =
ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true));
Settings::values.touchscreen.rotation_angle =
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
Settings::values.touchscreen.diameter_x =
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15));
Settings::values.touchscreen.diameter_y =
static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
}
void Config::ReadAudioValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
ReadCategory(Settings::Category::Audio);
ReadCategory(Settings::Category::UiAudio);
EndGroup();
}
void Config::ReadControlValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
ReadCategory(Settings::Category::Controls);
Settings::values.players.SetGlobal(!IsCustomConfig());
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
ReadPlayerValues(p);
}
// Disable docked mode if handheld is selected
const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
if (controller_type == Settings::ControllerType::Handheld) {
Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
}
if (IsCustomConfig()) {
EndGroup();
return;
}
ReadTouchscreenValues();
ReadMotionTouchValues();
EndGroup();
}
void Config::ReadMotionTouchValues() {
int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) {
SetArrayIndex(i);
Settings::TouchFromButtonMap map;
map.name = ReadStringSetting(std::string("name"), std::string("default"));
const int num_touch_maps = BeginArray(std::string("entries"));
map.buttons.reserve(num_touch_maps);
for (int j = 0; j < num_touch_maps; j++) {
SetArrayIndex(j);
std::string touch_mapping = ReadStringSetting(std::string("bind"));
map.buttons.emplace_back(std::move(touch_mapping));
}
EndArray(); // entries
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
}
} else {
Settings::values.touch_from_button_maps.emplace_back(
Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1;
}
EndArray(); // touch_from_button_maps
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
}
void Config::ReadCoreValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
ReadCategory(Settings::Category::Core);
EndGroup();
}
void Config::ReadDataStorageValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory")));
FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory")));
FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory")));
FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory")));
FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory")));
ReadCategory(Settings::Category::DataStorage);
EndGroup();
}
void Config::ReadDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
Settings::values.record_frame_times =
ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
ReadCategory(Settings::Category::Debugging);
ReadCategory(Settings::Category::DebuggingGraphics);
EndGroup();
}
void Config::ReadServiceValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
ReadCategory(Settings::Category::Services);
EndGroup();
}
void Config::ReadDisabledAddOnValues() {
// Custom config section
BeginGroup(std::string("DisabledAddOns"));
const int size = BeginArray(std::string(""));
for (int i = 0; i < size; ++i) {
SetArrayIndex(i);
const auto title_id = ReadIntegerSetting(std::string("title_id"), 0);
std::vector<std::string> out;
const int d_size = BeginArray("disabled");
for (int j = 0; j < d_size; ++j) {
SetArrayIndex(j);
out.push_back(ReadStringSetting(std::string("d"), std::string("")));
}
EndArray(); // d
Settings::values.disabled_addons.insert_or_assign(title_id, out);
}
EndArray(); // Base disabled addons array - Has no base key
EndGroup();
}
void Config::ReadMiscellaneousValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
ReadCategory(Settings::Category::Miscellaneous);
EndGroup();
}
void Config::ReadCpuValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
ReadCategory(Settings::Category::Cpu);
ReadCategory(Settings::Category::CpuDebug);
ReadCategory(Settings::Category::CpuUnsafe);
EndGroup();
}
void Config::ReadRendererValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
ReadCategory(Settings::Category::Renderer);
ReadCategory(Settings::Category::RendererAdvanced);
ReadCategory(Settings::Category::RendererDebug);
EndGroup();
}
void Config::ReadScreenshotValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
ReadCategory(Settings::Category::Screenshots);
FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir,
ReadStringSetting(std::string("screenshot_path"),
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
EndGroup();
}
void Config::ReadSystemValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::System));
ReadCategory(Settings::Category::System);
ReadCategory(Settings::Category::SystemAudio);
EndGroup();
}
void Config::ReadWebServiceValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
ReadCategory(Settings::Category::WebService);
EndGroup();
}
void Config::ReadNetworkValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
ReadCategory(Settings::Category::Network);
EndGroup();
}
void Config::ReadValues() {
if (global) {
ReadDataStorageValues();
ReadDebuggingValues();
ReadDisabledAddOnValues();
ReadNetworkValues();
ReadServiceValues();
ReadWebServiceValues();
ReadMiscellaneousValues();
}
ReadControlValues();
ReadCoreValues();
ReadCpuValues();
ReadRendererValues();
ReadAudioValues();
ReadSystemValues();
}
void Config::SavePlayerValues(const std::size_t player_index) {
std::string player_prefix;
if (type != ConfigType::InputProfile) {
player_prefix = std::string("player_").append(ToString(player_index)).append("_");
}
const auto& player = Settings::values.players.GetValue()[player_index];
if (IsCustomConfig()) {
if (player.profile_name.empty()) {
// No custom profile selected
return;
}
WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
std::make_optional(std::string("")));
}
WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
WriteSetting(std::string(player_prefix).append("connected"), player.connected,
std::make_optional(player_index == 0));
WriteSetting(std::string(player_prefix).append("vibration_enabled"),
player.vibration_enabled, std::make_optional(true));
WriteSetting(std::string(player_prefix).append("vibration_strength"),
player.vibration_strength, std::make_optional(100));
WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left,
std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right,
std::make_optional(Settings::JOYCON_BODY_NEON_RED));
WriteSetting(std::string(player_prefix).append("button_color_left"),
player.button_color_left,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
WriteSetting(std::string(player_prefix).append("button_color_right"),
player.button_color_right,
std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
}
}
void Config::SaveTouchscreenValues() {
const auto& touchscreen = Settings::values.touchscreen;
WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true));
WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
std::make_optional(static_cast<u32>(0)));
WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
std::make_optional(static_cast<u32>(15)));
WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
std::make_optional(static_cast<u32>(15)));
}
void Config::SaveMotionTouchValues() {
BeginArray(std::string("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
SetArrayIndex(static_cast<int>(p));
WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
std::make_optional(std::string("default")));
BeginArray(std::string("entries"));
for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
++q) {
SetArrayIndex(static_cast<int>(q));
WriteSetting(std::string("bind"),
Settings::values.touch_from_button_maps[p].buttons[q]);
}
EndArray(); // entries
}
EndArray(); // touch_from_button_maps
}
void Config::SaveValues() {
if (global) {
SaveDataStorageValues();
SaveDebuggingValues();
SaveDisabledAddOnValues();
SaveNetworkValues();
SaveWebServiceValues();
SaveMiscellaneousValues();
}
SaveControlValues();
SaveCoreValues();
SaveCpuValues();
SaveRendererValues();
SaveAudioValues();
SaveSystemValues();
WriteToIni();
}
void Config::SaveAudioValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Audio));
WriteCategory(Settings::Category::Audio);
WriteCategory(Settings::Category::UiAudio);
EndGroup();
}
void Config::SaveControlValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
WriteCategory(Settings::Category::Controls);
Settings::values.players.SetGlobal(!IsCustomConfig());
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
SavePlayerValues(p);
}
if (IsCustomConfig()) {
EndGroup();
return;
}
SaveTouchscreenValues();
SaveMotionTouchValues();
EndGroup();
}
void Config::SaveCoreValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Core));
WriteCategory(Settings::Category::Core);
EndGroup();
}
void Config::SaveDataStorageValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
WriteCategory(Settings::Category::DataStorage);
EndGroup();
}
void Config::SaveDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteCategory(Settings::Category::Debugging);
WriteCategory(Settings::Category::DebuggingGraphics);
EndGroup();
}
void Config::SaveNetworkValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
WriteCategory(Settings::Category::Network);
EndGroup();
}
void Config::SaveDisabledAddOnValues() {
// Custom config section
BeginGroup(std::string("DisabledAddOns"));
int i = 0;
BeginArray(std::string(""));
for (const auto& elem : Settings::values.disabled_addons) {
SetArrayIndex(i);
WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0)));
BeginArray(std::string("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
SetArrayIndex(static_cast<int>(j));
WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
}
EndArray(); // disabled
++i;
}
EndArray(); // Base disabled addons array - Has no base key
EndGroup();
}
void Config::SaveMiscellaneousValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous));
WriteCategory(Settings::Category::Miscellaneous);
EndGroup();
}
void Config::SaveCpuValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu));
WriteCategory(Settings::Category::Cpu);
WriteCategory(Settings::Category::CpuDebug);
WriteCategory(Settings::Category::CpuUnsafe);
EndGroup();
}
void Config::SaveRendererValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer));
WriteCategory(Settings::Category::Renderer);
WriteCategory(Settings::Category::RendererAdvanced);
WriteCategory(Settings::Category::RendererDebug);
EndGroup();
}
void Config::SaveScreenshotValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
WriteSetting(std::string("screenshot_path"),
FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
WriteCategory(Settings::Category::Screenshots);
EndGroup();
}
void Config::SaveSystemValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::System));
WriteCategory(Settings::Category::System);
WriteCategory(Settings::Category::SystemAudio);
EndGroup();
}
void Config::SaveWebServiceValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::WebService));
WriteCategory(Settings::Category::WebService);
EndGroup();
}
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false);
}
if (config->GetBoolValue(GetSection().c_str(),
std::string(full_key).append("\\default").c_str(), false)) {
return static_cast<bool>(default_value.value());
} else {
return config->GetBoolValue(GetSection().c_str(), full_key.c_str(),
static_cast<bool>(default_value.value()));
}
}
s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) {
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
return std::stoll(
std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
}
s64 result = 0;
if (config->GetBoolValue(GetSection().c_str(),
std::string(full_key).append("\\default").c_str(), true)) {
result = default_value.value();
} else {
result = std::stoll(std::string(config->GetValue(GetSection().c_str(), full_key.c_str(),
ToString(default_value.value()).c_str())));
}
return result;
}
double Config::ReadDoubleSetting(const std::string& key,
const std::optional<double> default_value) {
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0);
}
double result;
if (config->GetBoolValue(GetSection().c_str(),
std::string(full_key).append("\\default").c_str(), true)) {
result = default_value.value();
} else {
result =
config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value());
}
return result;
}
std::string Config::ReadStringSetting(const std::string& key,
const std::optional<std::string> default_value) {
std::string result;
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
result = config->GetValue(GetSection().c_str(), full_key.c_str(), "");
boost::replace_all(result, "\"", "");
return result;
}
if (config->GetBoolValue(GetSection().c_str(),
std::string(full_key).append("\\default").c_str(), true)) {
result = default_value.value();
} else {
result =
config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str());
}
boost::replace_all(result, "\"", "");
boost::replace_all(result, "//", "/");
return result;
}
template <typename Type>
void Config::WriteSetting(const std::string& key, const Type& value,
const std::optional<Type>& default_value,
const std::optional<bool>& use_global) {
std::string full_key = GetFullKey(key, false);
std::string saved_value;
std::string string_default;
if constexpr (std::is_same_v<Type, std::string>) {
saved_value.append(AdjustOutputString(value));
if (default_value.has_value()) {
string_default.append(AdjustOutputString(default_value.value()));
}
} else {
saved_value.append(AdjustOutputString(ToString(value)));
if (default_value.has_value()) {
string_default.append(ToString(default_value.value()));
}
}
if (default_value.has_value() && use_global.has_value()) {
if (!global) {
WriteSettingInternal(std::string(full_key).append("\\global"),
ToString(use_global.value()));
}
if (global || use_global.value() == false) {
WriteSettingInternal(std::string(full_key).append("\\default"),
ToString(string_default == saved_value));
WriteSettingInternal(full_key, saved_value);
}
} else if (default_value.has_value() && !use_global.has_value()) {
WriteSettingInternal(std::string(full_key).append("\\default"),
ToString(string_default == saved_value));
WriteSettingInternal(full_key, saved_value);
} else {
WriteSettingInternal(full_key, saved_value);
}
}
void Config::WriteSettingInternal(const std::string& key, const std::string& value) {
config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
}
void Config::Reload() {
ReadValues();
// To apply default value changes
SaveValues();
}
void Config::Save() {
SaveValues();
}
void Config::ClearControlPlayerValues() const {
// If key is an empty string, all keys in the current group() are removed.
const char* section = Settings::TranslateCategory(Settings::Category::Controls);
CSimpleIniA::TNamesDepend keys;
config->GetAllKeys(section, keys);
for (const auto& key : keys) {
if (std::string(config->GetValue(section, key.pItem)).empty()) {
config->Delete(section, key.pItem);
}
}
}
const std::string& Config::GetConfigFilePath() const {
return config_loc;
}
void Config::ReadCategory(const Settings::Category category) {
const auto& settings = FindRelevantList(category);
std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); });
}
void Config::WriteCategory(const Settings::Category category) {
const auto& settings = FindRelevantList(category);
std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); });
}
void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
if (!setting->Save() || (!setting->Switchable() && !global)) {
return;
}
const std::string key = AdjustKey(setting->GetLabel());
const std::string default_value(setting->DefaultToString());
bool use_global = true;
if (setting->Switchable() && !global) {
use_global =
ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
setting->SetGlobal(use_global);
}
if (global || !use_global) {
const bool is_default =
ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
if (!is_default) {
const std::string setting_string = ReadStringSetting(key, default_value);
setting->LoadString(setting_string);
} else {
// Empty string resets the Setting to default
setting->LoadString("");
}
}
}
void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
if (!setting->Save()) {
return;
}
std::string key = AdjustKey(setting->GetLabel());
if (setting->Switchable()) {
if (!global) {
WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
}
if (global || !setting->UsingGlobal()) {
WriteSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteSetting(key, setting->ToString());
}
} else if (global) {
WriteSetting(std::string(key).append("\\default"),
setting->ToString() == setting->DefaultToString());
WriteSetting(key, setting->ToString());
}
}
void Config::BeginGroup(const std::string& group) {
// You can't begin a group while reading/writing from a config array
ASSERT(array_stack.empty());
key_stack.push_back(AdjustKey(group));
}
void Config::EndGroup() {
// You can't end a group if you haven't started one yet
ASSERT(!key_stack.empty());
// You can't end a group when reading/writing from a config array
ASSERT(array_stack.empty());
key_stack.pop_back();
}
std::string Config::GetSection() {
if (key_stack.empty()) {
return std::string{""};
}
return key_stack.front();
}
std::string Config::GetGroup() const {
if (key_stack.size() <= 1) {
return std::string{""};
}
std::string key;
for (size_t i = 1; i < key_stack.size(); ++i) {
key.append(key_stack[i]).append("\\");
}
return key;
}
std::string Config::AdjustKey(const std::string& key) {
std::string adjusted_key(key);
boost::replace_all(adjusted_key, "/", "\\");
boost::replace_all(adjusted_key, " ", "%20");
return adjusted_key;
}
std::string Config::AdjustOutputString(const std::string& string) {
std::string adjusted_string(string);
boost::replace_all(adjusted_string, "\\", "/");
boost::replace_all(adjusted_string, "//", "/");
// Needed for backwards compatibility with QSettings deserialization
for (const auto& special_character : special_characters) {
if (adjusted_string.find(special_character) != std::string::npos) {
adjusted_string.insert(0, "\"");
adjusted_string.append("\"");
break;
}
}
return adjusted_string;
}
std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
if (array_stack.empty()) {
return std::string(GetGroup()).append(AdjustKey(key));
}
std::string array_key;
for (size_t i = 0; i < array_stack.size(); ++i) {
if (!array_stack[i].name.empty()) {
array_key.append(array_stack[i].name).append("\\");
}
if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) {
array_key.append(ToString(array_stack[i].index)).append("\\");
}
}
std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key));
return final_key;
}
int Config::BeginArray(const std::string& array) {
array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
const int size = config->GetLongValue(GetSection().c_str(),
GetFullKey(std::string("size"), true).c_str(), 0);
array_stack.back().size = size;
return size;
}
void Config::EndArray() {
// You can't end a config array before starting one
ASSERT(!array_stack.empty());
// Write out the size to config
if (key_stack.size() == 1 && array_stack.back().name.empty()) {
// Edge-case where the first array created doesn't have a name
config->SetValue(GetSection().c_str(), std::string("size").c_str(),
ToString(array_stack.back().size).c_str());
} else {
const auto key = GetFullKey(std::string("size"), true);
config->SetValue(GetSection().c_str(), key.c_str(),
ToString(array_stack.back().size).c_str());
}
array_stack.pop_back();
}
void Config::SetArrayIndex(const int index) {
// You can't set the array index if you haven't started one yet
ASSERT(!array_stack.empty());
const int array_index = index + 1;
// You can't exceed the known max size of the array by more than 1
ASSERT(array_stack.front().size + 1 >= array_index);
// Change the config array size to the current index since you may want
// to reduce the number of elements that you read back from the config
// in the future.
array_stack.back().size = array_index;
array_stack.back().index = array_index;
}

View File

@ -0,0 +1,204 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include "common/settings.h"
#include <SimpleIni.h>
#include <boost/algorithm/string/replace.hpp>
// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni
#undef LoadString
#undef CreateFile
#undef DeleteFile
#undef CopyFile
#undef CreateDirectory
#undef MoveFile
namespace Core {
class System;
}
class Config {
public:
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
virtual ~Config() = default;
void ClearControlPlayerValues() const;
[[nodiscard]] const std::string& GetConfigFilePath() const;
protected:
explicit Config(ConfigType config_type = ConfigType::GlobalConfig);
void Initialize(const std::string& config_name = "config");
void Initialize(std::optional<std::string> config_path);
void WriteToIni() const;
void SetUpIni();
[[nodiscard]] bool IsCustomConfig() const;
void Reload();
void Save();
/**
* Derived config classes must implement this so they can reload all platform-specific
* values and global ones.
*/
virtual void ReloadAllValues() = 0;
/**
* Derived config classes must implement this so they can save all platform-specific
* and global values.
*/
virtual void SaveAllValues() = 0;
void ReadValues();
void ReadPlayerValues(std::size_t player_index);
void ReadTouchscreenValues();
void ReadMotionTouchValues();
// Read functions bases off the respective config section names.
void ReadAudioValues();
void ReadControlValues();
void ReadCoreValues();
void ReadDataStorageValues();
void ReadDebuggingValues();
void ReadServiceValues();
void ReadDisabledAddOnValues();
void ReadMiscellaneousValues();
void ReadCpuValues();
void ReadRendererValues();
void ReadScreenshotValues();
void ReadSystemValues();
void ReadWebServiceValues();
void ReadNetworkValues();
// Read platform specific sections
virtual void ReadHidbusValues() = 0;
virtual void ReadDebugControlValues() = 0;
virtual void ReadPathValues() = 0;
virtual void ReadShortcutValues() = 0;
virtual void ReadUIValues() = 0;
virtual void ReadUIGamelistValues() = 0;
virtual void ReadUILayoutValues() = 0;
virtual void ReadMultiplayerValues() = 0;
void SaveValues();
void SavePlayerValues(std::size_t player_index);
void SaveTouchscreenValues();
void SaveMotionTouchValues();
// Save functions based off the respective config section names.
void SaveAudioValues();
void SaveControlValues();
void SaveCoreValues();
void SaveDataStorageValues();
void SaveDebuggingValues();
void SaveNetworkValues();
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SaveCpuValues();
void SaveRendererValues();
void SaveScreenshotValues();
void SaveSystemValues();
void SaveWebServiceValues();
// Save platform specific sections
virtual void SaveHidbusValues() = 0;
virtual void SaveDebugControlValues() = 0;
virtual void SavePathValues() = 0;
virtual void SaveShortcutValues() = 0;
virtual void SaveUIValues() = 0;
virtual void SaveUIGamelistValues() = 0;
virtual void SaveUILayoutValues() = 0;
virtual void SaveMultiplayerValues() = 0;
virtual std::vector<Settings::BasicSetting*>& FindRelevantList(Settings::Category category) = 0;
/**
* Reads a setting from the qt_config.
*
* @param key The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
bool ReadBooleanSetting(const std::string& key,
std::optional<bool> default_value = std::nullopt);
s64 ReadIntegerSetting(const std::string& key, std::optional<s64> default_value = std::nullopt);
double ReadDoubleSetting(const std::string& key,
std::optional<double> default_value = std::nullopt);
std::string ReadStringSetting(const std::string& key,
std::optional<std::string> default_value = std::nullopt);
/**
* Writes a setting to the qt_config.
*
* @param key The setting's idetentifier
* @param value Value of the setting
* @param default_value Default of the setting if not present in config
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
template <typename Type = int>
void WriteSetting(const std::string& key, const Type& value,
const std::optional<Type>& default_value = std::nullopt,
const std::optional<bool>& use_global = std::nullopt);
void WriteSettingInternal(const std::string& key, const std::string& value);
void ReadCategory(Settings::Category category);
void WriteCategory(Settings::Category category);
void ReadSettingGeneric(Settings::BasicSetting* setting);
void WriteSettingGeneric(const Settings::BasicSetting* setting);
template <typename T>
[[nodiscard]] std::string ToString(const T& value_) {
if constexpr (std::is_same_v<T, std::string>) {
return value_;
} else if constexpr (std::is_same_v<T, std::optional<u32>>) {
return value_.has_value() ? std::to_string(*value_) : "none";
} else if constexpr (std::is_same_v<T, bool>) {
return value_ ? "true" : "false";
} else {
return std::to_string(static_cast<s64>(value_));
}
}
void BeginGroup(const std::string& group);
void EndGroup();
std::string GetSection();
[[nodiscard]] std::string GetGroup() const;
static std::string AdjustKey(const std::string& key);
static std::string AdjustOutputString(const std::string& string);
std::string GetFullKey(const std::string& key, bool skipArrayIndex);
int BeginArray(const std::string& array);
void EndArray();
void SetArrayIndex(int index);
const ConfigType type;
std::unique_ptr<CSimpleIniA> config;
std::string config_loc;
const bool global;
private:
inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*',
'|', ';', '\'', '\"', ',', '<', '.',
'>', '?', '`', '~', '='};
struct ConfigArray {
std::string name;
int size;
int index;
};
std::vector<ConfigArray> array_stack;
std::vector<std::string> key_stack;
};

View File

@ -15,6 +15,7 @@ add_library(video_core STATIC
buffer_cache/buffer_cache.cpp buffer_cache/buffer_cache.cpp
buffer_cache/buffer_cache.h buffer_cache/buffer_cache.h
buffer_cache/memory_tracker_base.h buffer_cache/memory_tracker_base.h
buffer_cache/usage_tracker.h
buffer_cache/word_manager.h buffer_cache/word_manager.h
cache_types.h cache_types.h
cdma_pusher.cpp cdma_pusher.cpp

View File

@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() {
if (!channel_state) { if (!channel_state) {
return; return;
} }
runtime.TickFrame(slot_buffers);
// Calculate hits and shots and move hit bits to the right // Calculate hits and shots and move hit bits to the right
const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(),
@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
for (const IntervalType& add_interval : tmp_intervals) { for (const IntervalType& add_interval : tmp_intervals) {
common_ranges.add(add_interval); common_ranges.add(add_interval);
} }
runtime.CopyBuffer(dest_buffer, src_buffer, copies); const auto& copy = copies[0];
src_buffer.MarkUsage(copy.src_offset, copy.size);
dest_buffer.MarkUsage(copy.dst_offset, copy.size);
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
if (has_new_downloads) { if (has_new_downloads) {
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
} }
@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
common_ranges.subtract(subtract_interval); common_ranges.subtract(subtract_interval);
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
auto& dest_buffer = slot_buffers[buffer]; Buffer& dest_buffer = slot_buffers[buffer];
const u32 offset = dest_buffer.Offset(*cpu_dst_address); const u32 offset = dest_buffer.Offset(*cpu_dst_address);
runtime.ClearBuffer(dest_buffer, offset, size, value); runtime.ClearBuffer(dest_buffer, offset, size, value);
dest_buffer.MarkUsage(offset, size);
return true; return true;
} }
@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1); async_downloads += std::make_pair(base_interval, 1);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy); normalized_copies.push_back(second_copy);
} }
@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
// Have in mind the staging buffer offset for the copy // Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset; copy.dst_offset += download_staging.offset;
const std::array copies{copy}; const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, Buffer& buffer = slot_buffers[buffer_id];
false); buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
} }
runtime.PostCopyBarrier(); runtime.PostCopyBarrier();
runtime.Finish(); runtime.Finish();
@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(), std::memcpy(upload_staging.mapped_span.data(),
draw_state.inline_index_draw_indexes.data(), size); draw_state.inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies); runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true);
} else { } else {
buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
} }
@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() {
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
runtime.BindIndexBuffer(buffer, new_offset, size); runtime.BindIndexBuffer(buffer, new_offset, size);
} else { } else {
buffer.MarkUsage(offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count, draw_state.index_buffer.first, draw_state.index_buffer.count,
buffer, offset, size); buffer, offset, size);
@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, binding.size);
host_bindings.buffers.push_back(&buffer); host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset); host_bindings.offsets.push_back(offset);
@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
} }
buffer.MarkUsage(offset, size);
if constexpr (NEEDS_BIND_UNIFORM_INDEX) { if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size);
} else { } else {
@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
SynchronizeBuffer(buffer, binding.cpu_addr, size); SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
if (is_written) { if (is_written) {
@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format; const PixelFormat format = binding.format;
buffer.MarkUsage(offset, size);
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) {
runtime.BindImageBuffer(buffer, offset, size, format); runtime.BindImageBuffer(buffer, offset, size, format);
@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
host_bindings.buffers.push_back(&buffer); host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset); host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size); host_bindings.sizes.push_back(size);
} }
if (host_bindings.buffers.size() > 0) { if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings); runtime.BindTransformFeedbackBuffers(host_bindings);
@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
SynchronizeBuffer(buffer, binding.cpu_addr, size); SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
if constexpr (NEEDS_BIND_UNIFORM_INDEX) { if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
++binding_index; ++binding_index;
@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
SynchronizeBuffer(buffer, binding.cpu_addr, size); SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
buffer.MarkUsage(offset, size);
const bool is_written = const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0; ((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format; const PixelFormat format = binding.format;
buffer.MarkUsage(offset, size);
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) {
runtime.BindImageBuffer(buffer, offset, size, format); runtime.BindImageBuffer(buffer, offset, size, format);
@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) {
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
} }
const BufferId buffer_id = FindBuffer(*cpu_addr, size);
channel_state->vertex_buffers[index] = Binding{ channel_state->vertex_buffers[index] = Binding{
.cpu_addr = *cpu_addr, .cpu_addr = *cpu_addr,
.size = size, .size = size,
.buffer_id = FindBuffer(*cpu_addr, size), .buffer_id = buffer_id,
}; };
} }
@ -1406,7 +1423,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
.dst_offset = dst_base_offset, .dst_offset = dst_base_offset,
.size = overlap.SizeBytes(), .size = overlap.SizeBytes(),
}); });
runtime.CopyBuffer(new_buffer, overlap, copies); new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size);
runtime.CopyBuffer(new_buffer, overlap, copies, true);
DeleteBuffer(overlap_id, true); DeleteBuffer(overlap_id, true);
} }
@ -1419,7 +1437,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
const u32 size = static_cast<u32>(overlap.end - overlap.begin); const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
auto& new_buffer = slot_buffers[new_buffer_id]; auto& new_buffer = slot_buffers[new_buffer_id];
runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); const size_t size_bytes = new_buffer.SizeBytes();
runtime.ClearBuffer(new_buffer, 0, size_bytes, 0);
new_buffer.MarkUsage(0, size_bytes);
for (const BufferId overlap_id : overlap.ids) { for (const BufferId overlap_id : overlap.ids) {
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
} }
@ -1472,11 +1492,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
template <class P> template <class P>
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) {
return SynchronizeBufferImpl(buffer, cpu_addr, size);
}
template <class P>
bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) {
boost::container::small_vector<BufferCopy, 4> copies; boost::container::small_vector<BufferCopy, 4> copies;
u64 total_size_bytes = 0; u64 total_size_bytes = 0;
u64 largest_copy = 0; u64 largest_copy = 0;
@ -1498,51 +1513,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s
return false; return false;
} }
template <class P>
bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) {
boost::container::small_vector<BufferCopy, 4> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
IntervalSet found_sets{};
auto make_copies = [&] {
for (auto& interval : found_sets) {
const std::size_t sub_size = interval.upper() - interval.lower();
const VAddr cpu_addr_ = interval.lower();
copies.push_back(BufferCopy{
.src_offset = total_size_bytes,
.dst_offset = cpu_addr_ - buffer.CpuAddr(),
.size = sub_size,
});
total_size_bytes += sub_size;
largest_copy = std::max<u64>(largest_copy, sub_size);
}
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
};
memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) {
const VAddr base_adr = cpu_addr_out;
const VAddr end_adr = base_adr + range_size;
const IntervalType add_interval{base_adr, end_adr};
found_sets.add(add_interval);
});
if (found_sets.empty()) {
return true;
}
const IntervalType search_interval{cpu_addr, cpu_addr + size};
auto it = common_ranges.lower_bound(search_interval);
auto it_end = common_ranges.upper_bound(search_interval);
if (it == common_ranges.end()) {
make_copies();
return false;
}
while (it != it_end) {
found_sets.subtract(*it);
it++;
}
make_copies();
return false;
}
template <class P> template <class P>
void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies) { std::span<BufferCopy> copies) {
@ -1591,7 +1561,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
// Apply the staging offset // Apply the staging offset
copy.src_offset += upload_staging.offset; copy.src_offset += upload_staging.offset;
} }
runtime.CopyBuffer(buffer, upload_staging.buffer, copies); const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
} }
} }
@ -1633,7 +1604,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_
}}; }};
u8* const src_pointer = upload_staging.mapped_span.data(); u8* const src_pointer = upload_staging.mapped_span.data();
std::memcpy(src_pointer, inlined_buffer.data(), copy_size); std::memcpy(src_pointer, inlined_buffer.data(), copy_size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies); const bool can_reorder = runtime.CanReorderUpload(buffer, copies);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder);
} else { } else {
buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size));
} }
@ -1686,8 +1658,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
for (BufferCopy& copy : copies) { for (BufferCopy& copy : copies) {
// Modify copies to have the staging offset in mind // Modify copies to have the staging offset in mind
copy.dst_offset += download_staging.offset; copy.dst_offset += download_staging.offset;
buffer.MarkUsage(copy.src_offset, copy.size);
} }
runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true);
runtime.Finish(); runtime.Finish();
for (const BufferCopy& copy : copies) { for (const BufferCopy& copy : copies) {
const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset;

View File

@ -529,10 +529,6 @@ private:
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size);
bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size);
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
std::span<BufferCopy> copies); std::span<BufferCopy> copies);

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/alignment.h"
#include "common/common_types.h"
namespace VideoCommon {
class UsageTracker {
static constexpr size_t BYTES_PER_BIT_SHIFT = 6;
static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT;
static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT;
public:
explicit UsageTracker(size_t size) {
const size_t num_pages = (size >> PAGE_SHIFT) + 1;
pages.resize(num_pages, 0ULL);
}
void Reset() noexcept {
std::ranges::fill(pages, 0ULL);
}
void Track(u64 offset, u64 size) noexcept {
const size_t page = offset >> PAGE_SHIFT;
const size_t page_end = (offset + size) >> PAGE_SHIFT;
TrackPage(page, offset, size);
if (page == page_end) {
return;
}
for (size_t i = page + 1; i < page_end; i++) {
pages[i] = ~u64{0};
}
const size_t offset_end = offset + size;
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
}
[[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept {
const size_t page = offset >> PAGE_SHIFT;
const size_t page_end = (offset + size) >> PAGE_SHIFT;
if (IsPageUsed(page, offset, size)) {
return true;
}
for (size_t i = page + 1; i < page_end; i++) {
if (pages[i] != 0) {
return true;
}
}
const size_t offset_end = offset + size;
const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES);
return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned);
}
private:
void TrackPage(u64 page, u64 offset, u64 size) noexcept {
const size_t offset_in_page = offset % PAGE_BYTES;
const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
const size_t mask = ~u64{0} >> (64 - num_bits);
pages[page] |= (~u64{0} & mask) << first_bit;
}
bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept {
const size_t offset_in_page = offset % PAGE_BYTES;
const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT;
const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT;
const size_t mask = ~u64{0} >> (64 - num_bits);
const size_t mask2 = (~u64{0} & mask) << first_bit;
return (pages[page] & mask2) != 0;
}
private:
std::vector<u64> pages;
};
} // namespace VideoCommon

View File

@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
} }
void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier) { std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool) {
CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier);
} }
void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies) { std::span<const VideoCommon::BufferCopy> copies, bool) {
CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true);
} }
void BufferCacheRuntime::PreCopyBarrier() { void BufferCacheRuntime::PreCopyBarrier() {

View File

@ -30,6 +30,8 @@ public:
void MakeResident(GLenum access) noexcept; void MakeResident(GLenum access) noexcept;
void MarkUsage(u64 offset, u64 size) {}
[[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
[[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept {
@ -66,22 +68,29 @@ public:
[[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size);
bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) {
return false;
}
void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, void CopyBuffer(GLuint dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); std::span<const VideoCommon::BufferCopy> copies, bool barrier);
void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); std::span<const VideoCommon::BufferCopy> copies, bool barrier);
void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload = false);
void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
std::span<const VideoCommon::BufferCopy> copies); std::span<const VideoCommon::BufferCopy> copies, bool);
void PreCopyBarrier(); void PreCopyBarrier();
void PostCopyBarrier(); void PostCopyBarrier();
void Finish(); void Finish();
void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);

View File

@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
} // Anonymous namespace } // Anonymous namespace
Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {} : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
VAddr cpu_addr_, u64 size_bytes_) VAddr cpu_addr_, u64 size_bytes_)
: VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_),
device{&runtime.device}, buffer{ device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())},
CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { tracker{SizeBytes()} {
if (runtime.device.HasDebuggingToolAttached()) { if (runtime.device.HasDebuggingToolAttached()) {
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
} }
@ -355,12 +355,31 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
return device.CanReportMemoryUsage(); return device.CanReportMemoryUsage();
} }
void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
it->ResetUsageTracking();
}
}
void BufferCacheRuntime::Finish() { void BufferCacheRuntime::Finish() {
scheduler.Finish(); scheduler.Finish();
} }
bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer,
std::span<const VideoCommon::BufferCopy> copies) {
if (Settings::values.disable_buffer_reorder) {
return false;
}
const bool can_use_upload_cmdbuf =
std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) {
return !buffer.IsRegionUsed(copy.dst_offset, copy.size);
});
return can_use_upload_cmdbuf;
}
void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier) { std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload) {
if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
return; return;
} }
@ -376,9 +395,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
}; };
// Measuring a popular game, this number never exceeds the specified size once data is warmed up // Measuring a popular game, this number never exceeds the specified size once data is warmed up
boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);
if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) {
scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies](
vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies);
});
return;
}
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) {
if (barrier) { if (barrier) {

View File

@ -5,6 +5,7 @@
#include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h" #include "video_core/buffer_cache/memory_tracker_base.h"
#include "video_core/buffer_cache/usage_tracker.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@ -34,6 +35,18 @@ public:
return *buffer; return *buffer;
} }
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
return tracker.IsUsed(offset, size);
}
void MarkUsage(u64 offset, u64 size) noexcept {
tracker.Track(offset, size);
}
void ResetUsageTracking() noexcept {
tracker.Reset();
}
operator VkBuffer() const noexcept { operator VkBuffer() const noexcept {
return *buffer; return *buffer;
} }
@ -49,6 +62,7 @@ private:
const Device* device{}; const Device* device{};
vk::Buffer buffer; vk::Buffer buffer;
std::vector<BufferView> views; std::vector<BufferView> views;
VideoCommon::UsageTracker tracker;
}; };
class QuadArrayIndexBuffer; class QuadArrayIndexBuffer;
@ -67,6 +81,8 @@ public:
ComputePassDescriptorQueue& compute_pass_descriptor_queue, ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool); DescriptorPool& descriptor_pool);
void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
void Finish(); void Finish();
u64 GetDeviceLocalMemory() const; u64 GetDeviceLocalMemory() const;
@ -79,12 +95,15 @@ public:
[[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false);
bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies);
void FreeDeferredStagingBuffer(StagingBufferRef& ref); void FreeDeferredStagingBuffer(StagingBufferRef& ref);
void PreCopyBarrier(); void PreCopyBarrier();
void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); std::span<const VideoCommon::BufferCopy> copies, bool barrier,
bool can_reorder_upload = false);
void PostCopyBarrier(); void PostCopyBarrier();

View File

@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) {
Refresh(); Refresh();
} }
VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore wait_semaphore, u64 host_tick) { VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick) {
if (semaphore) { if (semaphore) {
return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore,
host_tick);
} else { } else {
return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick);
} }
} }
@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
}; };
VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore signal_semaphore,
VkSemaphore wait_semaphore, u64 host_tick) { VkSemaphore wait_semaphore, u64 host_tick) {
const VkSemaphore timeline_semaphore = *semaphore; const VkSemaphore timeline_semaphore = *semaphore;
@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
const std::array signal_values{host_tick, u64(0)}; const std::array signal_values{host_tick, u64(0)};
const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
const VkTimelineSemaphoreSubmitInfo timeline_si{ const VkTimelineSemaphoreSubmitInfo timeline_si{
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
.waitSemaphoreCount = num_wait_semaphores, .waitSemaphoreCount = num_wait_semaphores,
.pWaitSemaphores = &wait_semaphore, .pWaitSemaphores = &wait_semaphore,
.pWaitDstStageMask = wait_stage_masks.data(), .pWaitDstStageMask = wait_stage_masks.data(),
.commandBufferCount = 1, .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
.pCommandBuffers = cmdbuf.address(), .pCommandBuffers = cmdbuffers.data(),
.signalSemaphoreCount = num_signal_semaphores, .signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = signal_semaphores.data(), .pSignalSemaphores = signal_semaphores.data(),
}; };
@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
return device.GetGraphicsQueue().Submit(submit_info); return device.GetGraphicsQueue().Submit(submit_info);
} }
VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
VkSemaphore wait_semaphore, u64 host_tick) { vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick) {
const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; const u32 num_signal_semaphores = signal_semaphore ? 1 : 0;
const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; const u32 num_wait_semaphores = wait_semaphore ? 1 : 0;
const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};
const VkSubmitInfo submit_info{ const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr, .pNext = nullptr,
.waitSemaphoreCount = num_wait_semaphores, .waitSemaphoreCount = num_wait_semaphores,
.pWaitSemaphores = &wait_semaphore, .pWaitSemaphores = &wait_semaphore,
.pWaitDstStageMask = wait_stage_masks.data(), .pWaitDstStageMask = wait_stage_masks.data(),
.commandBufferCount = 1, .commandBufferCount = static_cast<u32>(cmdbuffers.size()),
.pCommandBuffers = cmdbuf.address(), .pCommandBuffers = cmdbuffers.data(),
.signalSemaphoreCount = num_signal_semaphores, .signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = &signal_semaphore, .pSignalSemaphores = &signal_semaphore,
}; };

View File

@ -52,14 +52,16 @@ public:
void Wait(u64 tick); void Wait(u64 tick);
/// Submits the device graphics queue, updating the tick as necessary /// Submits the device graphics queue, updating the tick as necessary
VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore wait_semaphore, u64 host_tick); VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick);
private: private:
VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore wait_semaphore, u64 host_tick); VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, u64 host_tick);
VkSemaphore wait_semaphore, u64 host_tick); VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf,
VkSemaphore signal_semaphore, VkSemaphore wait_semaphore,
u64 host_tick);
void WaitThread(std::stop_token token); void WaitThread(std::stop_token token);

View File

@ -22,11 +22,12 @@ namespace Vulkan {
MICROPROFILE_DECLARE(Vulkan_WaitForWorker); MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
vk::CommandBuffer upload_cmdbuf) {
auto command = first; auto command = first;
while (command != nullptr) { while (command != nullptr) {
auto next = command->GetNext(); auto next = command->GetNext();
command->Execute(cmdbuf); command->Execute(cmdbuf, upload_cmdbuf);
command->~Command(); command->~Command();
command = next; command = next;
} }
@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
// Perform the work, tracking whether the chunk was a submission // Perform the work, tracking whether the chunk was a submission
// before executing. // before executing.
const bool has_submit = work->HasSubmit(); const bool has_submit = work->HasSubmit();
work->ExecuteAll(current_cmdbuf); work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf);
// If the chunk was a submission, reallocate the command buffer. // If the chunk was a submission, reallocate the command buffer.
if (has_submit) { if (has_submit) {
@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() {
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr, .pInheritanceInfo = nullptr,
}); });
current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
current_upload_cmdbuf.Begin({
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
});
} }
u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
InvalidateState(); InvalidateState();
const u64 signal_value = master_semaphore->NextTick(); const u64 signal_value = master_semaphore->NextTick();
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value,
this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) {
static constexpr VkMemoryBarrier WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
};
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
upload_cmdbuf.End();
cmdbuf.End(); cmdbuf.End();
if (on_submit) { if (on_submit) {
@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
std::scoped_lock lock{submit_mutex}; std::scoped_lock lock{submit_mutex};
switch (const VkResult result = master_semaphore->SubmitQueue( switch (const VkResult result = master_semaphore->SubmitQueue(
cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) {
case VK_SUCCESS: case VK_SUCCESS:
break; break;
case VK_ERROR_DEVICE_LOST: case VK_ERROR_DEVICE_LOST:

View File

@ -80,7 +80,8 @@ public:
/// Send work to a separate thread. /// Send work to a separate thread.
template <typename T> template <typename T>
void Record(T&& command) { requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer>
void RecordWithUploadBuffer(T&& command) {
if (chunk->Record(command)) { if (chunk->Record(command)) {
return; return;
} }
@ -88,6 +89,15 @@ public:
(void)chunk->Record(command); (void)chunk->Record(command);
} }
template <typename T>
requires std::is_invocable_v<T, vk::CommandBuffer>
void Record(T&& c) {
this->RecordWithUploadBuffer(
[command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) {
command(cmdbuf);
});
}
/// Returns the current command buffer tick. /// Returns the current command buffer tick.
[[nodiscard]] u64 CurrentTick() const noexcept { [[nodiscard]] u64 CurrentTick() const noexcept {
return master_semaphore->CurrentTick(); return master_semaphore->CurrentTick();
@ -119,7 +129,7 @@ private:
public: public:
virtual ~Command() = default; virtual ~Command() = default;
virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0;
Command* GetNext() const { Command* GetNext() const {
return next; return next;
@ -142,8 +152,8 @@ private:
TypedCommand(TypedCommand&&) = delete; TypedCommand(TypedCommand&&) = delete;
TypedCommand& operator=(TypedCommand&&) = delete; TypedCommand& operator=(TypedCommand&&) = delete;
void Execute(vk::CommandBuffer cmdbuf) const override { void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override {
command(cmdbuf); command(cmdbuf, upload_cmdbuf);
} }
private: private:
@ -152,7 +162,7 @@ private:
class CommandChunk final { class CommandChunk final {
public: public:
void ExecuteAll(vk::CommandBuffer cmdbuf); void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf);
template <typename T> template <typename T>
bool Record(T& command) { bool Record(T& command) {
@ -228,6 +238,7 @@ private:
VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr;
vk::CommandBuffer current_cmdbuf; vk::CommandBuffer current_cmdbuf;
vk::CommandBuffer current_upload_cmdbuf;
std::unique_ptr<CommandChunk> chunk; std::unique_ptr<CommandChunk> chunk;
std::function<void()> on_submit; std::function<void()> on_submit;

View File

@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) {
UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
scheduler.Record([&](vk::CommandBuffer& cmdbuf) { scheduler.Record([&](vk::CommandBuffer cmdbuf) {
for (auto& images : m_dynamic_images) { for (auto& images : m_dynamic_images) {
for (size_t i = 0; i < MaxDynamicImage; i++) { for (size_t i = 0; i < MaxDynamicImage; i++) {
ClearColorImage(cmdbuf, *images.images[i]); ClearColorImage(cmdbuf, *images.images[i]);
@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_
UpdateDescriptorSets(source_image_view, image_index); UpdateDescriptorSets(source_image_view, image_index);
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,

View File

@ -36,6 +36,10 @@ public:
StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false);
void FreeDeferred(StagingBufferRef& ref); void FreeDeferred(StagingBufferRef& ref);
[[nodiscard]] VkBuffer StreamBuf() const noexcept {
return *stream_buffer;
}
void TickFrame(); void TickFrame();
private: private:

View File

@ -138,6 +138,10 @@ public:
return Iterator(this, SlotId{SlotId::INVALID_INDEX}); return Iterator(this, SlotId{SlotId::INVALID_INDEX});
} }
[[nodiscard]] size_t size() const noexcept {
return values_capacity - free_list.size();
}
private: private:
struct NonTrivialDummy { struct NonTrivialDummy {
NonTrivialDummy() noexcept {} NonTrivialDummy() noexcept {}

View File

@ -1101,6 +1101,10 @@ public:
return &handle; return &handle;
} }
VkCommandBuffer operator*() const noexcept {
return handle;
}
void Begin(const VkCommandBufferBeginInfo& begin_info) const { void Begin(const VkCommandBufferBeginInfo& begin_info) const {
Check(dld->vkBeginCommandBuffer(handle, &begin_info)); Check(dld->vkBeginCommandBuffer(handle, &begin_info));
} }

View File

@ -38,8 +38,6 @@ add_executable(yuzu
compatdb.ui compatdb.ui
compatibility_list.cpp compatibility_list.cpp
compatibility_list.h compatibility_list.h
configuration/config.cpp
configuration/config.h
configuration/configuration_shared.cpp configuration/configuration_shared.cpp
configuration/configuration_shared.h configuration/configuration_shared.h
configuration/configure.ui configuration/configure.ui
@ -147,6 +145,8 @@ add_executable(yuzu
configuration/shared_translation.h configuration/shared_translation.h
configuration/shared_widget.cpp configuration/shared_widget.cpp
configuration/shared_widget.h configuration/shared_widget.h
configuration/qt_config.cpp
configuration/qt_config.h
debugger/console.cpp debugger/console.cpp
debugger/console.h debugger/console.h
debugger/controller.cpp debugger/controller.cpp
@ -344,7 +344,7 @@ endif()
create_target_directory_groups(yuzu) create_target_directory_groups(yuzu)
target_link_libraries(yuzu PRIVATE common core input_common network video_core) target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)

File diff suppressed because it is too large Load Diff

View File

@ -1,179 +0,0 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <string>
#include <QMetaType>
#include <QVariant>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "yuzu/uisettings.h"
class QSettings;
namespace Core {
class System;
}
class Config {
public:
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Reload();
void Save();
void ReadControlPlayerValue(std::size_t player_index);
void SaveControlPlayerValue(std::size_t player_index);
void ClearControlPlayerValues();
const std::string& GetConfigFilePath() const;
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<int, 2> default_stick_mod;
static const std::array<int, 2> default_ringcon_analogs;
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
static const std::array<UISettings::Shortcut, 23> default_hotkeys;
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
static constexpr UISettings::Theme default_theme{
#ifdef _WIN32
UISettings::Theme::DarkColorful
#else
UISettings::Theme::DefaultColorful
#endif
};
private:
void Initialize(const std::string& config_name);
bool IsCustomConfig();
void ReadValues();
void ReadPlayerValue(std::size_t player_index);
void ReadDebugValues();
void ReadKeyboardValues();
void ReadMouseValues();
void ReadTouchscreenValues();
void ReadMotionTouchValues();
void ReadHidbusValues();
void ReadIrCameraValues();
// Read functions bases off the respective config section names.
void ReadAudioValues();
void ReadControlValues();
void ReadCoreValues();
void ReadDataStorageValues();
void ReadDebuggingValues();
void ReadServiceValues();
void ReadDisabledAddOnValues();
void ReadMiscellaneousValues();
void ReadPathValues();
void ReadCpuValues();
void ReadRendererValues();
void ReadScreenshotValues();
void ReadShortcutValues();
void ReadSystemValues();
void ReadUIValues();
void ReadUIGamelistValues();
void ReadUILayoutValues();
void ReadWebServiceValues();
void ReadMultiplayerValues();
void ReadNetworkValues();
void SaveValues();
void SavePlayerValue(std::size_t player_index);
void SaveDebugValues();
void SaveMouseValues();
void SaveTouchscreenValues();
void SaveMotionTouchValues();
void SaveHidbusValues();
void SaveIrCameraValues();
// Save functions based off the respective config section names.
void SaveAudioValues();
void SaveControlValues();
void SaveCoreValues();
void SaveDataStorageValues();
void SaveDebuggingValues();
void SaveNetworkValues();
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SavePathValues();
void SaveCpuValues();
void SaveRendererValues();
void SaveScreenshotValues();
void SaveShortcutValues();
void SaveSystemValues();
void SaveUIValues();
void SaveUIGamelistValues();
void SaveUILayoutValues();
void SaveWebServiceValues();
void SaveMultiplayerValues();
/**
* Reads a setting from the qt_config.
*
* @param name The setting's identifier
* @param default_value The value to use when the setting is not already present in the config
*/
QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
/**
* Writes a setting to the qt_config.
*
* @param name The setting's idetentifier
* @param value Value of the setting
* @param default_value Default of the setting if not present in qt_config
* @param use_global Specifies if the custom or global config should be in use, for custom
* configs
*/
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
bool use_global);
void ReadCategory(Settings::Category category);
void WriteCategory(Settings::Category category);
void ReadSettingGeneric(Settings::BasicSetting* const setting);
void WriteSettingGeneric(Settings::BasicSetting* const setting) const;
const ConfigType type;
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
const bool global;
};
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
Q_DECLARE_METATYPE(Settings::CpuAccuracy);
Q_DECLARE_METATYPE(Settings::GpuAccuracy);
Q_DECLARE_METATYPE(Settings::FullscreenMode);
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
Q_DECLARE_METATYPE(Settings::ResolutionSetup);
Q_DECLARE_METATYPE(Settings::ScalingFilter);
Q_DECLARE_METATYPE(Settings::AntiAliasing);
Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);

View File

@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
std::map<u32, QWidget*> hold; std::map<u32, QWidget*> hold;
auto push = [&](Settings::Category category) { auto push_settings = [&](Settings::Category category) {
for (auto* setting : Settings::values.linkage.by_category[category]) { for (auto* setting : Settings::values.linkage.by_category[category]) {
settings.push_back(setting); settings.push_back(setting);
} }
};
auto push_ui_settings = [&](Settings::Category category) {
for (auto* setting : UISettings::values.linkage.by_category[category]) { for (auto* setting : UISettings::values.linkage.by_category[category]) {
settings.push_back(setting); settings.push_back(setting);
} }
}; };
push(Settings::Category::Audio); push_settings(Settings::Category::Audio);
push(Settings::Category::SystemAudio); push_settings(Settings::Category::SystemAudio);
push_ui_settings(Settings::Category::UiAudio);
for (auto* setting : settings) { for (auto* setting : settings) {
auto* widget = builder.BuildWidget(setting, apply_funcs); auto* widget = builder.BuildWidget(setting, apply_funcs);

View File

@ -10,10 +10,10 @@
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QTimer> #include <QTimer>
#include "common/settings.h"
#include "input_common/drivers/camera.h" #include "input_common/drivers/camera.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "ui_configure_camera.h" #include "ui_configure_camera.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_camera.h" #include "yuzu/configuration/configure_camera.h"
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)

View File

@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
ui->disable_buffer_reorder->setEnabled(runtime_lock);
ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock); ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock); ui->enable_shader_feedback->setEnabled(runtime_lock);
@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked();
Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();

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