Compare commits

..

1 Commits

Author SHA1 Message Date
bf99e05f33 Android #120 2023-11-02 00:57:37 +00:00
387 changed files with 15778 additions and 22475 deletions

View File

@ -3,4 +3,4 @@
[codespell] [codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nce,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink

View File

@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# CRLF -> LF
90aa937593e53a5d5e070fb623b228578b0b225f

View File

@ -40,11 +40,11 @@ jobs:
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
- name: Build - name: Build
run: ./.ci/scripts/android/build.sh run: ./.ci/scripts/android/build.sh
- name: Copy and sign artifacts
env: env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }} ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }} ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
- name: Copy artifacts
run: ./.ci/scripts/android/upload.sh run: ./.ci/scripts/android/upload.sh
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

9
.gitmodules vendored
View File

@ -4,6 +4,9 @@
[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
@ -58,9 +61,3 @@
[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
[submodule "oaknut"]
path = externals/oaknut
url = https://github.com/merryhime/oaknut

View File

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

@ -0,0 +1,27 @@
# 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,7 +1,5 @@
| Pull Request | Commit | Title | Author | Merged? | | 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 |
| [12074](https://github.com/yuzu-emu/yuzu//pull/12074) | [`643250874`](https://github.com/yuzu-emu/yuzu//pull/12074/files) | Implement Native Code Execution (NCE) | [GPUCode](https://github.com/GPUCode/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -6,8 +6,3 @@ file_filter = <lang>.ts
source_file = en.ts source_file = en.ts
source_lang = en source_lang = en
type = QT type = QT
[o:yuzu-emulator:p:yuzu:r:yuzu-android]
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID

View File

@ -20,10 +20,6 @@ if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
endif() endif()
# Dynarmic # Dynarmic
if (ARCHITECTURE_arm64 AND NOT TARGET merry::oaknut)
add_subdirectory(oaknut)
endif()
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic) if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic)
set(DYNARMIC_IGNORE_ASSERTS ON) set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic) add_subdirectory(dynarmic)
@ -38,6 +34,11 @@ 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)
@ -294,6 +295,3 @@ 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)

13
externals/inih/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,13 @@
# 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

Submodule externals/inih/inih added at 9cecf0643d

1
externals/oaknut vendored

Submodule externals/oaknut deleted from 918bd94f02

1
externals/simpleini vendored

Submodule externals/simpleini deleted from 382ddbb4b9

View File

@ -21,7 +21,7 @@ if (MSVC)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN) add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects are built with Unicode support. # Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE) add_definitions(-DUNICODE -D_UNICODE)
# /W4 - Level 4 warnings # /W4 - Level 4 warnings
@ -54,11 +54,11 @@ if (MSVC)
/GT /GT
# Modules # Modules
/experimental:module- # Explicitly disable module support due to conflicts with precompiled headers. /experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
# External headers diagnostics # External headers diagnostics
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them. /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
# Warnings # Warnings
/W4 /W4
@ -187,7 +187,6 @@ 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,6 +219,7 @@ 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,6 +230,8 @@ 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)
@ -250,7 +252,7 @@ object NativeLibrary {
external fun reloadKeys(): Boolean external fun reloadKeys(): Boolean
external fun initializeSystem(reload: Boolean) external fun initializeSystem()
external fun defaultCPUCore(): Int external fun defaultCPUCore(): Int
@ -299,11 +301,6 @@ object NativeLibrary {
*/ */
external fun getPerfStats(): DoubleArray external fun getPerfStats(): DoubleArray
/**
* Returns the current CPU backend.
*/
external fun getCpuBackend(): String
/** /**
* Notifies the core emulation that the orientation has changed. * Notifies the core emulation that the orientation has changed.
*/ */

View File

@ -11,7 +11,6 @@ import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
@ -50,7 +49,6 @@ class YuzuApplication : Application() {
DirectoryInitialization.start() DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters() GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo() NativeLibrary.logDeviceInfo()
Log.logDeviceInfo()
createNotificationChannels() createNotificationChannels()
} }

View File

@ -107,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) {
Toast.makeText( Toast.makeText(
this, this,
getString( getString(
@ -373,10 +373,8 @@ 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 && isEmulationActive BooleanSetting.PICTURE_IN_PICTURE.boolean
) )
} }
setPictureInPictureParams(pictureInPictureParamsBuilder.build()) setPictureInPictureParams(pictureInPictureParamsBuilder.build())

View File

@ -22,16 +22,12 @@ 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
@ -96,34 +92,28 @@ class GameAdapter(private val activity: AppCompatActivity) :
data = Uri.parse(holder.game.path) data = Uri.parse(holder.game.path)
} }
activity.lifecycleScope.launch { val layerDrawable = ResourcesCompat.getDrawable(
withContext(Dispatchers.IO) { YuzuApplication.appContext.resources,
val layerDrawable = ResourcesCompat.getDrawable( R.drawable.shortcut,
YuzuApplication.appContext.resources, null
R.drawable.shortcut, ) as LayerDrawable
null layerDrawable.setDrawableByLayerId(
) as LayerDrawable R.id.shortcut_foreground,
layerDrawable.setDrawableByLayerId( GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
R.id.shortcut_foreground, )
GameIconUtils.getGameIcon(activity, holder.game) val inset = YuzuApplication.appContext.resources
.toDrawable(YuzuApplication.appContext.resources) .getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
) )
val inset = YuzuApplication.appContext.resources )
.getDimensionPixelSize(R.dimen.icon_inset) .setIntent(openIntent)
layerDrawable.setLayerInset(1, inset, inset, inset, inset) .build()
val shortcut = ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, 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

@ -10,7 +10,6 @@ enum class IntSetting(
override val category: Settings.Category, override val category: Settings.Category,
override val androidDefault: Int? = null override val androidDefault: Int? = null
) : AbstractIntSetting { ) : AbstractIntSetting {
CPU_BACKEND("cpu_backend", Settings.Category.Cpu),
CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu), CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
REGION_INDEX("region_index", Settings.Category.System), REGION_INDEX("region_index", Settings.Category.System),
LANGUAGE_INDEX("language_index", Settings.Category.System), LANGUAGE_INDEX("language_index", Settings.Category.System),

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.utils.NativeConfig import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
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()
NativeConfig.saveSettings() SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
} else { } else {
// TODO: Save custom game settings // TODO: Save custom game settings
Toast.makeText( Toast.makeText(
@ -82,6 +82,7 @@ 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,13 +3,10 @@
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

@ -73,19 +73,10 @@ abstract class SettingsItem(
R.string.frame_limit_slider, R.string.frame_limit_slider,
R.string.frame_limit_slider_description, R.string.frame_limit_slider_description,
1, 1,
400, 200,
"%" "%"
) )
) )
put(
SingleChoiceSetting(
IntSetting.CPU_BACKEND,
R.string.cpu_backend,
0,
R.array.cpuBackendArm64Names,
R.array.cpuBackendArm64Values
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.CPU_ACCURACY, IntSetting.CPU_ACCURACY,

View File

@ -3,14 +3,11 @@
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(
@StringRes titleId: Int, titleId: Int,
@StringRes descriptionId: Int, 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,6 +21,7 @@ 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
@ -164,12 +165,11 @@ 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")
} }
NativeConfig.initializeConfig() NativeLibrary.reloadSettings()
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,

View File

@ -20,6 +20,7 @@ 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
@ -67,9 +68,15 @@ 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 {
@ -87,6 +94,17 @@ 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) {
@ -94,6 +112,8 @@ 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
@ -109,6 +129,11 @@ 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
@ -119,9 +144,10 @@ 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 = leftInsets mlpSettingsList.leftMargin = sideMargin + leftInsets
mlpSettingsList.rightMargin = rightInsets mlpSettingsList.rightMargin = sideMargin + rightInsets
binding.listSettings.layoutParams = mlpSettingsList binding.listSettings.layoutParams = mlpSettingsList
binding.listSettings.updatePadding( binding.listSettings.updatePadding(
bottom = barInsets.bottom bottom = barInsets.bottom

View File

@ -3,6 +3,7 @@
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
@ -31,6 +32,8 @@ 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]!!
@ -50,6 +53,7 @@ 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)
@ -71,53 +75,30 @@ 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(
SubmenuSetting( RunnableSetting(R.string.reset_to_default, 0, false) {
R.string.preferences_system, settingsViewModel.setShouldShowResetSettingsDialog(true)
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)
@ -135,7 +116,6 @@ 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)
@ -269,8 +249,6 @@ 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_BACKEND.key)
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,7 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import 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
@ -17,19 +16,6 @@ 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,7 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View import android.view.View
import 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
@ -16,19 +15,6 @@ 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,8 +3,15 @@
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.
@ -12,6 +19,41 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
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 mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams
mlpToolbar.leftMargin = leftInsets mlpAppBar.leftMargin = leftInsets
mlpToolbar.rightMargin = rightInsets mlpAppBar.rightMargin = rightInsets
binding.toolbarAbout.layoutParams = mlpToolbar binding.appbarAbout.layoutParams = mlpAppBar
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets mlpScrollAbout.leftMargin = leftInsets

View File

@ -10,6 +10,7 @@ import android.content.DialogInterface
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -154,6 +155,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
binding.surfaceEmulation.holder.addCallback(this) binding.surfaceEmulation.holder.addCallback(this)
binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
binding.drawerLayout.addDrawerListener(object : DrawerListener { binding.drawerLayout.addDrawerListener(object : DrawerListener {
@ -310,8 +312,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.surfaceInputOverlay) ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator) ViewUtils.hideView(binding.loadingIndicator)
emulationState.updateSurface()
// Setup overlay // Setup overlay
updateShowFpsOverlay() updateShowFpsOverlay()
} }
@ -412,14 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val FRAMETIME = 2 val FRAMETIME = 2
val SPEED = 3 val SPEED = 3
perfStatsUpdater = { perfStatsUpdater = {
if (emulationViewModel.emulationStarted.value) { if (emulationViewModel.emulationStarted.value == true) {
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend() if (perfStats[FPS] > 0 && _binding != null) {
if (_binding != null) { binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
binding.showFpsText.text =
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
} }
} }
perfStatsUpdateHandler.post(perfStatsUpdater!!) perfStatsUpdateHandler.post(perfStatsUpdater!!)
@ -464,6 +462,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
// Restrict emulation and overlays to the top of the screen // Restrict emulation and overlays to the top of the screen
binding.emulationContainer.layoutParams.height = it.bounds.top binding.emulationContainer.layoutParams.height = it.bounds.top
binding.overlayContainer.layoutParams.height = it.bounds.top
// Restrict input and menu drawer to the bottom of the screen // Restrict input and menu drawer to the bottom of the screen
binding.inputContainer.layoutParams.height = it.bounds.bottom binding.inputContainer.layoutParams.height = it.bounds.bottom
binding.inGameMenu.layoutParams.height = it.bounds.bottom binding.inGameMenu.layoutParams.height = it.bounds.bottom
@ -477,6 +476,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (!isFolding) { if (!isFolding) {
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
isInFoldableLayout = false isInFoldableLayout = false
updateOrientation() updateOrientation()
@ -484,6 +484,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
binding.emulationContainer.requestLayout() binding.emulationContainer.requestLayout()
binding.inputContainer.requestLayout() binding.inputContainer.requestLayout()
binding.overlayContainer.requestLayout()
binding.inGameMenu.requestLayout() binding.inGameMenu.requestLayout()
} }
@ -709,6 +710,24 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
v.setPadding(left, cutInsets.top, right, 0) v.setPadding(left, cutInsets.top, right, 0)
// Ensure FPS text doesn't get cut off by rounded display corners
val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
if (cutInsets.left == 0) {
binding.showFpsText.setPadding(
sidePadding,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
} else {
binding.showFpsText.setPadding(
cutInsets.left,
cutInsets.top,
cutInsets.right,
cutInsets.bottom
)
}
windowInsets windowInsets
} }
} }
@ -785,13 +804,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
@Synchronized
fun updateSurface() {
if (surface != null) {
NativeLibrary.surfaceChanged(surface)
}
}
@Synchronized @Synchronized
fun clearSurface() { fun clearSurface() {
if (surface == null) { if (surface == null) {

View File

@ -21,8 +21,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class InstallableFragment : Fragment() { class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null private var _binding: FragmentInstallablesBinding? = null
@ -80,15 +78,7 @@ class InstallableFragment : Fragment() {
R.string.manage_save_data, R.string.manage_save_data,
R.string.import_export_saves_description, R.string.import_export_saves_description,
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
export = { export = { mainActivity.exportSave() }
mainActivity.exportSaves.launch(
"yuzu saves - ${
LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
}.zip"
)
}
) )
} else { } else {
Installable( Installable(

View File

@ -40,10 +40,8 @@ class SettingsSearchFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
} }
override fun onCreateView( override fun onCreateView(
@ -57,6 +55,7 @@ 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

@ -18,8 +18,8 @@ class Game(
val version: String = "", val version: String = "",
val isHomebrew: Boolean = false val isHomebrew: Boolean = false
) : Parcelable { ) : Parcelable {
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${path}_LastPlayed" val keyLastPlayedTime get() = "${programId}_LastPlayed"
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is Game) { if (other !is Game) {

View File

@ -29,6 +29,9 @@ 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)
@ -54,6 +57,10 @@ 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

@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.ui.main
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.DocumentsContract
import android.view.View import android.view.View
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager import android.view.WindowManager
@ -19,6 +20,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
@ -39,6 +41,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@ -50,6 +53,9 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
@ -67,6 +73,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Get first subfolder in saves folder (should be the user folder) // Get first subfolder in saves folder (should be the user folder)
val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
private var lastZipCreated: File? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
@ -396,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else { } else {
firmwarePath.deleteRecursively() firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true) cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem()
getString(R.string.save_file_imported_success) getString(R.string.save_file_imported_success)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -625,7 +632,6 @@ 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
@ -643,39 +649,82 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
// Reinitialize relevant data // Reinitialize relevant data
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem()
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)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
} }
/**
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
* @return true if the zip file is successfully created, false otherwise.
*/
private fun zipSave(): Boolean {
try {
val tempFolder = File(getPublicFilesDir().canonicalPath, "temp")
tempFolder.mkdirs()
val saveFolder = File(savesFolderRoot)
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
val result = FileUtil.zipFromInternalStorage(
saveFolder,
savesFolderRoot,
BufferedOutputStream(FileOutputStream(outputZipFile))
)
if (result == TaskState.Failed) {
return false
}
lastZipCreated = outputZipFile
} catch (e: Exception) {
return false
}
return true
}
/** /**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent. * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/ */
val exportSaves = registerForActivityResult( fun exportSave() {
ActivityResultContracts.CreateDocument("application/zip") CoroutineScope(Dispatchers.IO).launch {
) { result -> val wasZipCreated = zipSave()
if (result == null) { val lastZipFile = lastZipCreated
return@registerForActivityResult if (!wasZipCreated || lastZipFile == null) {
} withContext(Dispatchers.Main) {
Toast.makeText(
IndeterminateProgressDialogFragment.newInstance( this@MainActivity,
this, getString(R.string.export_save_failed),
R.string.save_files_exporting, Toast.LENGTH_LONG
false ).show()
) { }
val zipResult = FileUtil.zipFromInternalStorage( return@launch
File(savesFolderRoot),
savesFolderRoot,
BufferedOutputStream(contentResolver.openOutputStream(result))
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
} }
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
this@MainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, "application/zip")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startForResultExportSave.launch(
Intent.createChooser(
intent,
getString(R.string.share_save_file)
)
)
}
}
} }
private val startForResultExportSave = private val startForResultExportSave =

View File

@ -15,8 +15,7 @@ object DirectoryInitialization {
fun start() { fun start() {
if (!areDirectoriesReady) { if (!areDirectoriesReady) {
initializeInternalStorage() initializeInternalStorage()
NativeLibrary.initializeSystem(false) NativeLibrary.initializeSystem()
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,13 +76,12 @@ object GameIconUtils {
imageLoader.enqueue(request) imageLoader.enqueue(request)
} }
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap { fun getGameIcon(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.execute(request) return imageLoader.executeBlocking(request)
.drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
} }
} }

View File

@ -3,8 +3,6 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.os.Build
object Log { object Log {
// Tracks whether we should share the old log or the current log // Tracks whether we should share the old log or the current log
var gameLaunched = false var gameLaunched = false
@ -18,14 +16,4 @@ object Log {
external fun error(message: String) external fun error(message: String)
external fun critical(message: String) external fun critical(message: String)
fun logDeviceInfo() {
info("Device Manufacturer - ${Build.MANUFACTURER}")
info("Device Model - ${Build.MODEL}")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
info("SoC Model - ${Build.SOC_MODEL}")
}
info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
}
} }

View File

@ -27,7 +27,7 @@ object MemoryUtil {
const val Pb = Tb * 1024 const val Pb = Tb * 1024
const val Eb = Pb * 1024 const val Eb = Pb * 1024
private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = private fun bytesToSizeUnit(size: Float): String =
when { when {
size < Kb -> { size < Kb -> {
context.getString( context.getString(
@ -39,59 +39,63 @@ object MemoryUtil {
size < Mb -> { size < Mb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, (size / Kb).hundredths,
context.getString(R.string.memory_kilobyte) context.getString(R.string.memory_kilobyte)
) )
} }
size < Gb -> { size < Gb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, (size / Mb).hundredths,
context.getString(R.string.memory_megabyte) context.getString(R.string.memory_megabyte)
) )
} }
size < Tb -> { size < Tb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, (size / Gb).hundredths,
context.getString(R.string.memory_gigabyte) context.getString(R.string.memory_gigabyte)
) )
} }
size < Pb -> { size < Pb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, (size / Tb).hundredths,
context.getString(R.string.memory_terabyte) context.getString(R.string.memory_terabyte)
) )
} }
size < Eb -> { size < Eb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, (size / Pb).hundredths,
context.getString(R.string.memory_petabyte) context.getString(R.string.memory_petabyte)
) )
} }
else -> { else -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, (size / Eb).hundredths,
context.getString(R.string.memory_exabyte) context.getString(R.string.memory_exabyte)
) )
} }
} }
val totalMemory: Float // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
// the potential error created by memInfo.totalMem
private val totalMemory: Float
get() { get() {
val memInfo = ActivityManager.MemoryInfo() val memInfo = ActivityManager.MemoryInfo()
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
getMemoryInfo(memInfo) getMemoryInfo(memInfo)
} }
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { return ceil(
memInfo.advertisedMem.toFloat() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
} else { memInfo.advertisedMem.toFloat()
memInfo.totalMem.toFloat() } else {
} memInfo.totalMem.toFloat()
}
)
} }
fun isLessThan(minimum: Int, size: Float): Boolean = fun isLessThan(minimum: Int, size: Float): Boolean =
@ -105,7 +109,5 @@ object MemoryUtil {
else -> totalMemory < Kb && totalMemory < minimum else -> totalMemory < Kb && totalMemory < minimum
} }
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory)
// the potential error created by memInfo.totalMem
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
} }

View File

@ -4,30 +4,6 @@
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,6 +6,9 @@ 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
@ -13,17 +16,15 @@ add_library(yuzu-android SHARED
native.cpp native.cpp
native.h native.h
native_config.cpp native_config.cpp
android_settings.cpp uisettings.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 frontend_common) target_link_libraries(yuzu-android PRIVATE audio_core common core input_common)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih 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

@ -1,70 +0,0 @@
// 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

@ -1,41 +0,0 @@
// 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

@ -0,0 +1,330 @@
// 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

@ -0,0 +1,47 @@
// 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

@ -0,0 +1,511 @@
// 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,7 +9,6 @@
#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);
@ -58,13 +57,6 @@ 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,6 +61,4 @@ 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"
@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const {
return m_is_running && m_is_paused; return m_is_running && m_is_paused;
} }
const Core::PerfStatsResults& EmulationSession::PerfStats() { const Core::PerfStatsResults& EmulationSession::PerfStats() const {
m_perf_stats = m_system.GetAndResetPerfStats(); std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats; return m_perf_stats;
} }
@ -247,13 +247,11 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
} }
} }
void EmulationSession::InitializeSystem(bool reload) { void EmulationSession::InitializeSystem() {
if (!reload) { // Initialize logging system
// Initialize logging system Common::Log::Initialize();
Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::Start();
Common::Log::Start();
}
// Initialize filesystem. // Initialize filesystem.
m_system.SetFilesystem(m_vfs); m_system.SetFilesystem(m_vfs);
@ -372,6 +370,8 @@ 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);
@ -381,6 +381,11 @@ void EmulationSession::RunEmulation() {
break; break;
} }
} }
{
// Refresh performance stats.
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
m_perf_stats = m_system.GetAndResetPerfStats();
}
} }
} }
@ -662,13 +667,12 @@ 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) { // Create the default config.ini.
Config{};
// Initialize the emulated system. // Initialize the emulated system.
if (!reload) { EmulationSession::GetInstance().System().Initialize();
EmulationSession::GetInstance().System().Initialize(); EmulationSession::GetInstance().InitializeSystem();
}
EmulationSession::GetInstance().InitializeSystem(reload);
} }
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
@ -678,6 +682,17 @@ 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);
@ -694,14 +709,6 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
return j_stats; return j_stats;
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
if (Settings::IsNceEnabled()) {
return ToJString(env, "NCE");
}
return ToJString(env, "JIT");
}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env, void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
jclass clazz, jclass clazz,
jstring j_path) {} jstring j_path) {}

View File

@ -41,9 +41,9 @@ public:
void RunEmulation(); void RunEmulation();
void ShutdownEmulation(); void ShutdownEmulation();
const Core::PerfStatsResults& PerfStats(); const Core::PerfStatsResults& PerfStats() const;
void ConfigureFilesystemProvider(const std::string& filepath); void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem(bool reload); void InitializeSystem();
Core::SystemResultStatus InitializeEmulation(const std::string& filepath); Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
bool IsHandheldOnly(); bool IsHandheldOnly();
@ -52,10 +52,9 @@ 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:
@ -81,5 +80,6 @@ private:
// Synchronization // Synchronization
std::condition_variable_any m_cv; std::condition_variable_any m_cv;
mutable std::mutex m_perf_stats_mutex;
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
}; };

View File

@ -5,14 +5,11 @@
#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"
std::unique_ptr<AndroidConfig> config; #include "uisettings.h"
template <typename T> template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@ -31,22 +28,6 @@ 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

@ -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 "android_settings.h" #include "uisettings.h"
namespace AndroidSettings { namespace AndroidSettings {

View File

@ -13,7 +13,7 @@ struct Values {
Settings::Linkage linkage; Settings::Linkage linkage;
// Android // Android
Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture", Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture",
Settings::Category::Android}; Settings::Category::Android};
Settings::Setting<s32> screen_layout{linkage, Settings::Setting<s32> screen_layout{linkage,
5, 5,

View File

@ -1,9 +0,0 @@
<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

@ -1,9 +0,0 @@
<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

@ -1,9 +0,0 @@
<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

@ -1,9 +0,0 @@
<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

@ -1,233 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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_marginBottom="24dp" android:layout_marginVertical="12dp"
android:layout_marginHorizontal="12dp" android:layout_marginHorizontal="16dp"
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="150dp" android:layout_width="250dp"
android:layout_height="150dp" android:layout_height="250dp"
android:layout_marginTop="24dp" android:layout_marginTop="20dp"
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

@ -134,21 +134,18 @@
<FrameLayout <FrameLayout
android:id="@+id/overlay_container" android:id="@+id/overlay_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView <TextView
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
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"
android:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
android:paddingHorizontal="20dp"
android:textColor="@android:color/white"
android:shadowColor="@android:color/black" android:shadowColor="@android:color/black"
android:shadowRadius="3" android:textColor="@android:color/white"
android:textSize="12sp"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</FrameLayout> </FrameLayout>

View File

@ -14,14 +14,13 @@
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="96dp" android:layout_width="128dp"
android:layout_height="96dp" android:layout_height="128dp"
android:layout_marginVertical="32dp" android:layout_margin="64dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_full" /> android:src="@drawable/ic_yuzu_full" />

View File

@ -127,7 +127,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingVertical="4dp" android:paddingVertical="4dp"
app:checkedChip="@id/chip_recently_played"
app:chipSpacingHorizontal="12dp" app:chipSpacingHorizontal="12dp"
app:singleLine="true" app:singleLine="true"
app:singleSelection="true"> app:singleSelection="true">

View File

@ -10,59 +10,41 @@
android:focusable="true" android:focusable="true"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="72dp" android:minHeight="72dp"
android:padding="16dp"> android:padding="@dimen/spacing_large">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="vertical">
<ImageView <com.google.android.material.textview.MaterialTextView
android:id="@+id/icon" android:id="@+id/text_setting_name"
android:layout_width="24dp" style="@style/TextAppearance.Material3.HeadlineMedium"
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:orientation="vertical"> android:textAlignment="viewStart"
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_name" android:id="@+id/text_setting_description"
style="@style/TextAppearance.Material3.HeadlineMedium" 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:textAlignment="viewStart" android:layout_marginTop="@dimen/spacing_small"
android:textSize="17sp" android:textAlignment="viewStart"
app:lineHeight="22dp" tools:text="@string/app_disclaimer" />
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_value"
style="@style/TextAppearance.Material3.BodySmall" style="@style/TextAppearance.Material3.LabelMedium"
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"
tools:text="@string/app_disclaimer" /> android:textStyle="bold"
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,7 +8,9 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:minHeight="72dp" android:minHeight="72dp"
android:padding="16dp"> android:paddingVertical="@dimen/spacing_large"
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"
@ -22,7 +24,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="24dp" android:layout_marginEnd="@dimen/spacing_large"
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">
@ -33,7 +35,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="17sp" android:textSize="16sp"
app:lineHeight="28dp" app:lineHeight="28dp"
tools:text="@string/frame_limit_enable" /> tools:text="@string/frame_limit_enable" />

View File

@ -7,8 +7,7 @@
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:paddingTop="16dp" android:paddingVertical="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,6 +2,7 @@
<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>
@ -12,6 +13,7 @@
</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>
@ -175,24 +177,6 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="cpuBackendArm64Names">
<item>@string/cpu_backend_dynarmic</item>
<item>@string/cpu_backend_nce</item>
</string-array>
<integer-array name="cpuBackendArm64Values">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="cpuBackendX86Names">
<item>@string/cpu_backend_dynarmic</item>
</string-array>
<integer-array name="cpuBackendX86Values">
<item>0</item>
</integer-array>
<string-array name="cpuAccuracyNames"> <string-array name="cpuAccuracyNames">
<item>@string/auto</item> <item>@string/auto</item>
<item>@string/cpu_accuracy_accurate</item> <item>@string/cpu_accuracy_accurate</item>

View File

@ -91,7 +91,6 @@
<string name="manage_save_data">Manage save data</string> <string name="manage_save_data">Manage save data</string>
<string name="manage_save_data_description">Save data found. Please select an option below.</string> <string name="manage_save_data_description">Save data found. Please select an option below.</string>
<string name="import_export_saves_description">Import or export save files</string> <string name="import_export_saves_description">Import or export save files</string>
<string name="save_files_exporting">Exporting save files…</string>
<string name="save_file_imported_success">Imported successfully</string> <string name="save_file_imported_success">Imported successfully</string>
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string> <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@ -185,7 +184,6 @@
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string> <string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Limit speed percent</string> <string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. 100% is the normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="cpu_backend">CPU backend</string>
<string name="cpu_accuracy">CPU accuracy</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@ -242,7 +240,6 @@
<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>
@ -258,7 +255,6 @@
<string name="cancelling">Cancelling</string> <string name="cancelling">Cancelling</string>
<string name="install">Install</string> <string name="install">Install</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="export_success">Exported successfully</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>
@ -275,14 +271,10 @@
<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>
@ -417,10 +409,6 @@
<string name="ratio_force_sixteen_ten">Force 16:10</string> <string name="ratio_force_sixteen_ten">Force 16:10</string>
<string name="ratio_stretch">Stretch to window</string> <string name="ratio_stretch">Stretch to window</string>
<!-- CPU Backend -->
<string name="cpu_backend_dynarmic">Dynarmic (Slow)</string>
<string name="cpu_backend_nce">Native code execution (NCE)</string>
<!-- CPU Accuracy --> <!-- CPU Accuracy -->
<string name="cpu_accuracy_accurate">Accurate</string> <string name="cpu_accuracy_accurate">Accurate</string>
<string name="cpu_accuracy_unsafe">Unsafe</string> <string name="cpu_accuracy_unsafe">Unsafe</string>

View File

@ -1,107 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decode_object.h" #include "audio_core/adsp/apps/opus/opus_decode_object.h"
#include "common/assert.h" #include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder { namespace AudioCore::ADSP::OpusDecoder {
namespace { namespace {
bool IsValidChannelCount(u32 channel_count) { bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2; return channel_count == 1 || channel_count == 2;
} }
} // namespace } // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (!IsValidChannelCount(channel_count)) { if (!IsValidChannelCount(channel_count)) {
return 0; return 0;
} }
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
} }
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer); auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2); auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeObjectMagic) { if (new_decoder->magic == DecodeObjectMagic) {
if (!new_decoder->initialized || if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) { (new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true; new_decoder->state_valid = true;
} }
} else { } else {
new_decoder->initialized = false; new_decoder->initialized = false;
new_decoder->state_valid = true; new_decoder->state_valid = true;
} }
return *new_decoder; return *new_decoder;
} }
s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
if (initialized) { if (initialized) {
return OPUS_OK; return OPUS_OK;
} }
// Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
// it in this class. Nintendo does not allocate memory, which is why we have a workbuffer // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
// provided. // provided.
// We could use _create and have libopus allocate it for us, but then we have to separately // We could use _create and have libopus allocate it for us, but then we have to separately
// track which decoder is being used between this and multistream in order to call the correct // track which decoder is being used between this and multistream in order to call the correct
// destroy from the host side. // destroy from the host side.
// This is a bit cringe, but is safe as these objects are only ever initialized inside the given // This is a bit cringe, but is safe as these objects are only ever initialized inside the given
// workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
decoder = (LibOpusDecoder*)(this + 1); decoder = (LibOpusDecoder*)(this + 1);
s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
if (ret == OPUS_OK) { if (ret == OPUS_OK) {
magic = DecodeObjectMagic; magic = DecodeObjectMagic;
initialized = true; initialized = true;
state_valid = true; state_valid = true;
self = this; self = this;
final_range = 0; final_range = 0;
} }
return ret; return ret;
} }
s32 OpusDecodeObject::Shutdown() { s32 OpusDecodeObject::Shutdown() {
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
if (initialized) { if (initialized) {
magic = 0x0; magic = 0x0;
initialized = false; initialized = false;
state_valid = false; state_valid = false;
self = nullptr; self = nullptr;
final_range = 0; final_range = 0;
decoder = nullptr; decoder = nullptr;
} }
return OPUS_OK; return OPUS_OK;
} }
s32 OpusDecodeObject::ResetDecoder() { s32 OpusDecodeObject::ResetDecoder() {
return opus_decoder_ctl(decoder, OPUS_RESET_STATE); return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
} }
s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
u64 input_data, u64 input_data_size) { u64 input_data, u64 input_data_size) {
ASSERT(initialized); ASSERT(initialized);
out_sample_count = 0; out_sample_count = 0;
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
auto ret_code_or_samples = opus_decode( auto ret_code_or_samples = opus_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) { if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples; return ret_code_or_samples;
} }
out_sample_count = ret_code_or_samples; out_sample_count = ret_code_or_samples;
return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
} }
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder

View File

@ -30,9 +30,9 @@ bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count <= OpusStreamCountMax; return channel_count <= OpusStreamCountMax;
} }
bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) { bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) {
return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count; sterero_stream_count > 0 && sterero_stream_count <= total_stream_count;
} }
} // namespace } // namespace

View File

@ -1,111 +1,111 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" #include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
#include "common/assert.h" #include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder { namespace AudioCore::ADSP::OpusDecoder {
namespace { namespace {
bool IsValidChannelCount(u32 channel_count) { bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2; return channel_count == 1 || channel_count == 2;
} }
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
} }
} // namespace } // namespace
u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
u32 stereo_stream_count) { u32 stereo_stream_count) {
if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) + return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
} }
return 0; return 0;
} }
OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer); auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2); auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
if (new_decoder->magic == DecodeMultiStreamObjectMagic) { if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
if (!new_decoder->initialized || if (!new_decoder->initialized ||
(new_decoder->initialized && new_decoder->self == comparison)) { (new_decoder->initialized && new_decoder->self == comparison)) {
new_decoder->state_valid = true; new_decoder->state_valid = true;
} }
} else { } else {
new_decoder->initialized = false; new_decoder->initialized = false;
new_decoder->state_valid = true; new_decoder->state_valid = true;
} }
return *new_decoder; return *new_decoder;
} }
s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
u32 channel_count, u32 stereo_stream_count, u32 channel_count, u32 stereo_stream_count,
u8* mappings) { u8* mappings) {
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
if (initialized) { if (initialized) {
return OPUS_OK; return OPUS_OK;
} }
// See OpusDecodeObject::InitializeDecoder for an explanation of this // See OpusDecodeObject::InitializeDecoder for an explanation of this
decoder = (LibOpusMSDecoder*)(this + 1); decoder = (LibOpusMSDecoder*)(this + 1);
s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
stereo_stream_count, mappings); stereo_stream_count, mappings);
if (ret == OPUS_OK) { if (ret == OPUS_OK) {
magic = DecodeMultiStreamObjectMagic; magic = DecodeMultiStreamObjectMagic;
initialized = true; initialized = true;
state_valid = true; state_valid = true;
self = this; self = this;
final_range = 0; final_range = 0;
} }
return ret; return ret;
} }
s32 OpusMultiStreamDecodeObject::Shutdown() { s32 OpusMultiStreamDecodeObject::Shutdown() {
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
if (initialized) { if (initialized) {
magic = 0x0; magic = 0x0;
initialized = false; initialized = false;
state_valid = false; state_valid = false;
self = nullptr; self = nullptr;
final_range = 0; final_range = 0;
decoder = nullptr; decoder = nullptr;
} }
return OPUS_OK; return OPUS_OK;
} }
s32 OpusMultiStreamDecodeObject::ResetDecoder() { s32 OpusMultiStreamDecodeObject::ResetDecoder() {
return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
} }
s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
u64 output_data_size, u64 input_data, u64 input_data_size) { u64 output_data_size, u64 input_data, u64 input_data_size) {
ASSERT(initialized); ASSERT(initialized);
out_sample_count = 0; out_sample_count = 0;
if (!state_valid) { if (!state_valid) {
return OPUS_INVALID_STATE; return OPUS_INVALID_STATE;
} }
auto ret_code_or_samples = opus_multistream_decode( auto ret_code_or_samples = opus_multistream_decode(
decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size), decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0); reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
if (ret_code_or_samples < OPUS_OK) { if (ret_code_or_samples < OPUS_OK) {
return ret_code_or_samples; return ret_code_or_samples;
} }
out_sample_count = ret_code_or_samples; out_sample_count = ret_code_or_samples;
return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
} }
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder

View File

@ -1,179 +1,179 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/opus/decoder.h" #include "audio_core/opus/decoder.h"
#include "audio_core/opus/hardware_opus.h" #include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h" #include "audio_core/opus/parameters.h"
#include "common/alignment.h" #include "common/alignment.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
using namespace Service::Audio; using namespace Service::Audio;
namespace { namespace {
OpusPacketHeader ReverseHeader(OpusPacketHeader header) { OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
OpusPacketHeader out; OpusPacketHeader out;
out.size = Common::swap32(header.size); out.size = Common::swap32(header.size);
out.final_range = Common::swap32(header.final_range); out.final_range = Common::swap32(header.final_range);
return out; return out;
} }
} // namespace } // namespace
OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
: system{system_}, hardware_opus{hardware_opus_} {} : system{system_}, hardware_opus{hardware_opus_} {}
OpusDecoder::~OpusDecoder() { OpusDecoder::~OpusDecoder() {
if (decode_object_initialized) { if (decode_object_initialized) {
hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
} }
} }
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size) { u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size; shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size); shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true; shared_memory_mapped = true;
buffer_size = buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{0x600u}; size_t in_data_size{0x600u};
in_data = {out_data.data() - in_data_size, in_data_size}; in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE { ON_RESULT_FAILURE {
if (shared_memory_mapped) { if (shared_memory_mapped) {
shared_memory_mapped = false; shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
} }
}; };
R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
shared_buffer.get(), shared_buffer_size)); shared_buffer.get(), shared_buffer_size));
sample_rate = params.sample_rate; sample_rate = params.sample_rate;
channel_count = params.channel_count; channel_count = params.channel_count;
use_large_frame_size = params.use_large_frame_size; use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true; decode_object_initialized = true;
R_SUCCEED(); R_SUCCEED();
} }
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size; shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size); shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
shared_memory_mapped = true; shared_memory_mapped = true;
buffer_size = buffer_size =
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
in_data = {out_data.data() - in_data_size, in_data_size}; in_data = {out_data.data() - in_data_size, in_data_size};
ON_RESULT_FAILURE { ON_RESULT_FAILURE {
if (shared_memory_mapped) { if (shared_memory_mapped) {
shared_memory_mapped = false; shared_memory_mapped = false;
ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
} }
}; };
R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
params.sample_rate, params.channel_count, params.total_stream_count, params.sample_rate, params.channel_count, params.total_stream_count,
params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
shared_buffer_size)); shared_buffer_size));
sample_rate = params.sample_rate; sample_rate = params.sample_rate;
channel_count = params.channel_count; channel_count = params.channel_count;
total_stream_count = params.total_stream_count; total_stream_count = params.total_stream_count;
stereo_stream_count = params.stereo_stream_count; stereo_stream_count = params.stereo_stream_count;
use_large_frame_size = params.use_large_frame_size; use_large_frame_size = params.use_large_frame_size;
decode_object_initialized = true; decode_object_initialized = true;
R_SUCCEED(); R_SUCCEED();
} }
Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data, u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset) { std::span<u8> output_data, bool reset) {
u32 out_samples; u32 out_samples;
u64 time_taken{}; u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)}; OpusPacketHeader header{ReverseHeader(*header_p)};
R_UNLESS(in_data.size_bytes() >= header.size && R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall); ResultBufferTooSmall);
if (!shared_memory_mapped) { if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true; shared_memory_mapped = true;
} }
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
channel_count, in_data.data(), header.size, channel_count, in_data.data(), header.size,
shared_buffer.get(), time_taken, reset)); shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader); *out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples; *out_sample_count = out_samples;
if (out_time_taken) { if (out_time_taken) {
*out_time_taken = time_taken / 1000; *out_time_taken = time_taken / 1000;
} }
R_SUCCEED(); R_SUCCEED();
} }
Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
R_SUCCEED_IF(shared_memory_mapped); R_SUCCEED_IF(shared_memory_mapped);
shared_memory_mapped = true; shared_memory_mapped = true;
R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
} }
Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, u32* out_sample_count,
std::span<const u8> input_data, std::span<const u8> input_data,
std::span<u8> output_data, bool reset) { std::span<u8> output_data, bool reset) {
u32 out_samples; u32 out_samples;
u64 time_taken{}; u64 time_taken{};
R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)}; OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
header.size, input_data.size_bytes(), in_data.size_bytes()); header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size && R_UNLESS(in_data.size_bytes() >= header.size &&
header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
ResultBufferTooSmall); ResultBufferTooSmall);
if (!shared_memory_mapped) { if (!shared_memory_mapped) {
R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
shared_memory_mapped = true; shared_memory_mapped = true;
} }
std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
R_TRY(hardware_opus.DecodeInterleavedForMultiStream( R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
header.size, shared_buffer.get(), time_taken, reset)); header.size, shared_buffer.get(), time_taken, reset));
std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
*out_data_size = header.size + sizeof(OpusPacketHeader); *out_data_size = header.size + sizeof(OpusPacketHeader);
*out_sample_count = out_samples; *out_sample_count = out_samples;
if (out_time_taken) { if (out_time_taken) {
*out_time_taken = time_taken / 1000; *out_time_taken = time_taken / 1000;
} }
R_SUCCEED(); R_SUCCEED();
} }
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <span> #include <span>
#include "audio_core/opus/parameters.h" #include "audio_core/opus/parameters.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/errors.h" #include "core/hle/service/audio/errors.h"
namespace Core { namespace Core {
class System; class System;
} }
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
class HardwareOpus; class HardwareOpus;
class OpusDecoder { class OpusDecoder {
public: public:
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
~OpusDecoder(); ~OpusDecoder();
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size); u64 transfer_memory_size);
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size); u64 transfer_memory_size);
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
std::span<const u8> input_data, std::span<u8> output_data, bool reset); std::span<const u8> input_data, std::span<u8> output_data, bool reset);
Result SetContext([[maybe_unused]] std::span<const u8> context); Result SetContext([[maybe_unused]] std::span<const u8> context);
Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
u32* out_sample_count, std::span<const u8> input_data, u32* out_sample_count, std::span<const u8> input_data,
std::span<u8> output_data, bool reset); std::span<u8> output_data, bool reset);
private: private:
Core::System& system; Core::System& system;
HardwareOpus& hardware_opus; HardwareOpus& hardware_opus;
std::unique_ptr<u8[]> shared_buffer{}; std::unique_ptr<u8[]> shared_buffer{};
u64 shared_buffer_size; u64 shared_buffer_size;
std::span<u8> in_data{}; std::span<u8> in_data{};
std::span<u8> out_data{}; std::span<u8> out_data{};
u64 buffer_size{}; u64 buffer_size{};
s32 sample_rate{}; s32 sample_rate{};
s32 channel_count{}; s32 channel_count{};
bool use_large_frame_size{false}; bool use_large_frame_size{false};
s32 total_stream_count{}; s32 total_stream_count{};
s32 stereo_stream_count{}; s32 stereo_stream_count{};
bool shared_memory_mapped{false}; bool shared_memory_mapped{false};
bool decode_object_initialized{false}; bool decode_object_initialized{false};
}; };
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -1,102 +1,102 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/adsp/apps/opus/opus_decoder.h" #include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/opus/decoder_manager.h" #include "audio_core/opus/decoder_manager.h"
#include "common/alignment.h" #include "common/alignment.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
using namespace Service::Audio; using namespace Service::Audio;
namespace { namespace {
bool IsValidChannelCount(u32 channel_count) { bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2; return channel_count == 1 || channel_count == 2;
} }
bool IsValidMultiStreamChannelCount(u32 channel_count) { bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count > 0 && channel_count <= OpusStreamCountMax; return channel_count > 0 && channel_count <= OpusStreamCountMax;
} }
bool IsValidSampleRate(u32 sample_rate) { bool IsValidSampleRate(u32 sample_rate) {
return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
sample_rate == 24'000 || sample_rate == 48'000; sample_rate == 24'000 || sample_rate == 48'000;
} }
bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && return total_stream_count > 0 && stereo_stream_count > 0 &&
stereo_stream_count <= total_stream_count && stereo_stream_count <= total_stream_count &&
total_stream_count + stereo_stream_count <= channel_count; total_stream_count + stereo_stream_count <= channel_count;
} }
} // namespace } // namespace
OpusDecoderManager::OpusDecoderManager(Core::System& system_) OpusDecoderManager::OpusDecoderManager(Core::System& system_)
: system{system_}, hardware_opus{system} { : system{system_}, hardware_opus{system} {
for (u32 i = 0; i < MaxChannels; i++) { for (u32 i = 0; i < MaxChannels; i++) {
required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
} }
} }
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
OpusParametersEx ex{ OpusParametersEx ex{
.sample_rate = params.sample_rate, .sample_rate = params.sample_rate,
.channel_count = params.channel_count, .channel_count = params.channel_count,
.use_large_frame_size = false, .use_large_frame_size = false,
}; };
R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
} }
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
R_RETURN(GetWorkBufferSizeExEx(params, out_size)); R_RETURN(GetWorkBufferSizeExEx(params, out_size));
} }
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size += work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size + 0x600; out_size = work_buffer_size + 0x600;
R_SUCCEED(); R_SUCCEED();
} }
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
u64& out_size) { u64& out_size) {
OpusMultiStreamParametersEx ex{ OpusMultiStreamParametersEx ex{
.sample_rate = params.sample_rate, .sample_rate = params.sample_rate,
.channel_count = params.channel_count, .channel_count = params.channel_count,
.total_stream_count = params.total_stream_count, .total_stream_count = params.total_stream_count,
.stereo_stream_count = params.stereo_stream_count, .stereo_stream_count = params.stereo_stream_count,
.use_large_frame_size = false, .use_large_frame_size = false,
.mappings = {}, .mappings = {},
}; };
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
} }
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
u64& out_size) { u64& out_size) {
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
} }
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
u64& out_size) { u64& out_size) {
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
params.stereo_stream_count), params.stereo_stream_count),
ResultInvalidOpusSampleRate); ResultInvalidOpusSampleRate);
auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
params.total_stream_count, params.stereo_stream_count)}; params.total_stream_count, params.stereo_stream_count)};
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
work_buffer_size += work_buffer_size +=
Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
out_size = work_buffer_size; out_size = work_buffer_size;
R_SUCCEED(); R_SUCCEED();
} }
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "audio_core/opus/hardware_opus.h" #include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h" #include "audio_core/opus/parameters.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/audio/errors.h" #include "core/hle/service/audio/errors.h"
namespace Core { namespace Core {
class System; class System;
} }
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
class OpusDecoderManager { class OpusDecoderManager {
public: public:
OpusDecoderManager(Core::System& system); OpusDecoderManager(Core::System& system);
HardwareOpus& GetHardwareOpus() { HardwareOpus& GetHardwareOpus() {
return hardware_opus; return hardware_opus;
} }
Result GetWorkBufferSize(OpusParameters& params, u64& out_size); Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
private: private:
Core::System& system; Core::System& system;
HardwareOpus hardware_opus; HardwareOpus hardware_opus;
std::array<u64, MaxChannels> required_workbuffer_sizes{}; std::array<u64, MaxChannels> required_workbuffer_sizes{};
}; };
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -1,241 +1,241 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
#include "audio_core/opus/hardware_opus.h" #include "audio_core/opus/hardware_opus.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
namespace { namespace {
using namespace Service::Audio; using namespace Service::Audio;
static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
s32 error{static_cast<s32>(error_code)}; s32 error{static_cast<s32>(error_code)};
ASSERT(error <= OPUS_OK); ASSERT(error <= OPUS_OK);
switch (error) { switch (error) {
case OPUS_ALLOC_FAIL: case OPUS_ALLOC_FAIL:
R_THROW(ResultLibOpusAllocFail); R_THROW(ResultLibOpusAllocFail);
case OPUS_INVALID_STATE: case OPUS_INVALID_STATE:
R_THROW(ResultLibOpusInvalidState); R_THROW(ResultLibOpusInvalidState);
case OPUS_UNIMPLEMENTED: case OPUS_UNIMPLEMENTED:
R_THROW(ResultLibOpusUnimplemented); R_THROW(ResultLibOpusUnimplemented);
case OPUS_INVALID_PACKET: case OPUS_INVALID_PACKET:
R_THROW(ResultLibOpusInvalidPacket); R_THROW(ResultLibOpusInvalidPacket);
case OPUS_INTERNAL_ERROR: case OPUS_INTERNAL_ERROR:
R_THROW(ResultLibOpusInternalError); R_THROW(ResultLibOpusInternalError);
case OPUS_BUFFER_TOO_SMALL: case OPUS_BUFFER_TOO_SMALL:
R_THROW(ResultBufferTooSmall); R_THROW(ResultBufferTooSmall);
case OPUS_BAD_ARG: case OPUS_BAD_ARG:
R_THROW(ResultLibOpusBadArg); R_THROW(ResultLibOpusBadArg);
case OPUS_OK: case OPUS_OK:
R_RETURN(ResultSuccess); R_RETURN(ResultSuccess);
} }
UNREACHABLE(); UNREACHABLE();
} }
} // namespace } // namespace
HardwareOpus::HardwareOpus(Core::System& system_) HardwareOpus::HardwareOpus(Core::System& system_)
: system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
opus_decoder.SetSharedMemory(shared_memory); opus_decoder.SetSharedMemory(shared_memory);
} }
u64 HardwareOpus::GetWorkBufferSize(u32 channel) { u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
if (!opus_decoder.IsRunning()) { if (!opus_decoder.IsRunning()) {
return 0; return 0;
} }
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = channel; shared_memory.host_send_data[0] = channel;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
return 0; return 0;
} }
return shared_memory.dsp_return_data[0]; return shared_memory.dsp_return_data[0];
} }
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = total_stream_count; shared_memory.host_send_data[0] = total_stream_count;
shared_memory.host_send_data[1] = stereo_stream_count; shared_memory.host_send_data[1] = stereo_stream_count;
opus_decoder.Send(ADSP::Direction::DSP, opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
return 0; return 0;
} }
return shared_memory.dsp_return_data[0]; return shared_memory.dsp_return_data[0];
} }
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size) { u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate; shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count; shared_memory.host_send_data[3] = channel_count;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
} }
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 total_stream_count, u32 total_stream_count,
u32 stereo_stream_count, void* mappings, u32 stereo_stream_count, void* mappings,
void* buffer, u64 buffer_size) { void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
shared_memory.host_send_data[2] = sample_rate; shared_memory.host_send_data[2] = sample_rate;
shared_memory.host_send_data[3] = channel_count; shared_memory.host_send_data[3] = channel_count;
shared_memory.host_send_data[4] = total_stream_count; shared_memory.host_send_data[4] = total_stream_count;
shared_memory.host_send_data[5] = stereo_stream_count; shared_memory.host_send_data[5] = stereo_stream_count;
ASSERT(channel_count <= MaxChannels); ASSERT(channel_count <= MaxChannels);
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
opus_decoder.Send(ADSP::Direction::DSP, opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
} }
Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
"Expected Opus shutdown code {}, got {}", "Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
} }
Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
"Expected Opus shutdown code {}, got {}", "Expected Opus shutdown code {}, got {}",
ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
} }
Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count, void* input_data, u64 output_data_size, u32 channel_count, void* input_data,
u64 input_data_size, void* buffer, u64& out_time_taken, u64 input_data_size, void* buffer, u64& out_time_taken,
bool reset) { bool reset) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data; shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size; shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data; shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size; shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0; shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset; shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) { if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2]; out_time_taken = 1000 * shared_memory.dsp_return_data[2];
} }
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
} }
Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count, u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size, void* input_data, u64 input_data_size,
void* buffer, u64& out_time_taken, void* buffer, u64& out_time_taken,
bool reset) { bool reset) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = (u64)input_data; shared_memory.host_send_data[1] = (u64)input_data;
shared_memory.host_send_data[2] = input_data_size; shared_memory.host_send_data[2] = input_data_size;
shared_memory.host_send_data[3] = (u64)output_data; shared_memory.host_send_data[3] = (u64)output_data;
shared_memory.host_send_data[4] = output_data_size; shared_memory.host_send_data[4] = output_data_size;
shared_memory.host_send_data[5] = 0; shared_memory.host_send_data[5] = 0;
shared_memory.host_send_data[6] = reset; shared_memory.host_send_data[6] = reset;
opus_decoder.Send(ADSP::Direction::DSP, opus_decoder.Send(ADSP::Direction::DSP,
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
if (error_code == OPUS_OK) { if (error_code == OPUS_OK) {
out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
out_time_taken = 1000 * shared_memory.dsp_return_data[2]; out_time_taken = 1000 * shared_memory.dsp_return_data[2];
} }
R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
} }
Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::MapMemoryOK, msg); ADSP::OpusDecoder::Message::MapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
R_SUCCEED(); R_SUCCEED();
} }
Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer; shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size; shared_memory.host_send_data[1] = buffer_size;
opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
auto msg = opus_decoder.Receive(ADSP::Direction::Host); auto msg = opus_decoder.Receive(ADSP::Direction::Host);
if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
R_THROW(ResultInvalidOpusDSPReturnCode); R_THROW(ResultInvalidOpusDSPReturnCode);
} }
R_SUCCEED(); R_SUCCEED();
} }
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -1,45 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <opus.h> #include <opus.h>
#include "audio_core/adsp/apps/opus/opus_decoder.h" #include "audio_core/adsp/apps/opus/opus_decoder.h"
#include "audio_core/adsp/apps/opus/shared_memory.h" #include "audio_core/adsp/apps/opus/shared_memory.h"
#include "audio_core/adsp/mailbox.h" #include "audio_core/adsp/mailbox.h"
#include "core/hle/service/audio/errors.h" #include "core/hle/service/audio/errors.h"
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
class HardwareOpus { class HardwareOpus {
public: public:
HardwareOpus(Core::System& system); HardwareOpus(Core::System& system);
u64 GetWorkBufferSize(u32 channel); u64 GetWorkBufferSize(u32 channel);
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size); u64 buffer_size);
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 totaL_stream_count, u32 stereo_stream_count, u32 totaL_stream_count, u32 stereo_stream_count,
void* mappings, void* buffer, u64 buffer_size); void* mappings, void* buffer, u64 buffer_size);
Result ShutdownDecodeObject(void* buffer, u64 buffer_size); Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
u32 channel_count, void* input_data, u64 input_data_size, void* buffer, u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset); u64& out_time_taken, bool reset);
Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
u64 output_data_size, u32 channel_count, u64 output_data_size, u32 channel_count,
void* input_data, u64 input_data_size, void* buffer, void* input_data, u64 input_data_size, void* buffer,
u64& out_time_taken, bool reset); u64& out_time_taken, bool reset);
Result MapMemory(void* buffer, u64 buffer_size); Result MapMemory(void* buffer, u64 buffer_size);
Result UnmapMemory(void* buffer, u64 buffer_size); Result UnmapMemory(void* buffer, u64 buffer_size);
private: private:
Core::System& system; Core::System& system;
std::mutex mutex; std::mutex mutex;
ADSP::OpusDecoder::OpusDecoder& opus_decoder; ADSP::OpusDecoder::OpusDecoder& opus_decoder;
ADSP::OpusDecoder::SharedMemory shared_memory; ADSP::OpusDecoder::SharedMemory shared_memory;
}; };
} // namespace AudioCore::OpusDecoder } // namespace AudioCore::OpusDecoder

View File

@ -146,7 +146,7 @@ public:
return; return;
} }
SignalPause(); paused = true;
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;
} }
SignalPause(); paused = true;
SDL_PauseAudioDevice(device, 1); SDL_PauseAudioDevice(device, 1);
} }

View File

@ -282,19 +282,11 @@ 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 paused || queued_buffers < max_queue_size; }); [this]() { return 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 paused || queued_buffers < max_queue_size; }); [this] { return 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,12 +213,6 @@ 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

@ -52,7 +52,6 @@ add_library(common STATIC
fiber.cpp fiber.cpp
fiber.h fiber.h
fixed_point.h fixed_point.h
free_region_manager.h
fs/file.cpp fs/file.cpp
fs/file.h fs/file.h
fs/fs.cpp fs/fs.cpp
@ -167,13 +166,6 @@ if (WIN32)
target_link_libraries(common PRIVATE ntdll) target_link_libraries(common PRIVATE ntdll)
endif() endif()
if (NOT WIN32)
target_sources(common PRIVATE
signal_chain.cpp
signal_chain.h
)
endif()
if(ANDROID) if(ANDROID)
target_sources(common target_sources(common
PRIVATE PRIVATE

View File

@ -1,9 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ANDROID
#include <sys/system_properties.h>
#endif
#include "common/arm64/native_clock.h" #include "common/arm64/native_clock.h"
namespace Common::Arm64 { namespace Common::Arm64 {
@ -68,23 +65,7 @@ bool NativeClock::IsNative() const {
u64 NativeClock::GetHostCNTFRQ() { u64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0; u64 cntfrq_el0 = 0;
std::string_view board{""}; asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
#ifdef ANDROID
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
#endif
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0; return cntfrq_el0;
} }

View File

@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <boost/icl/interval_set.hpp>
namespace Common {
class FreeRegionManager {
public:
explicit FreeRegionManager() = default;
~FreeRegionManager() = default;
void SetAddressSpace(void* start, size_t size) {
this->FreeBlock(start, size);
}
std::pair<void*, size_t> FreeBlock(void* block_ptr, size_t size) {
std::scoped_lock lk(m_mutex);
// Check to see if we are adjacent to any regions.
auto start_address = reinterpret_cast<uintptr_t>(block_ptr);
auto end_address = start_address + size;
auto it = m_free_regions.find({start_address - 1, end_address + 1});
// If we are, join with them, ensuring we stay in bounds.
if (it != m_free_regions.end()) {
start_address = std::min(start_address, it->lower());
end_address = std::max(end_address, it->upper());
}
// Free the relevant region.
m_free_regions.insert({start_address, end_address});
// Return the adjusted pointers.
block_ptr = reinterpret_cast<void*>(start_address);
size = end_address - start_address;
return {block_ptr, size};
}
void AllocateBlock(void* block_ptr, size_t size) {
std::scoped_lock lk(m_mutex);
auto address = reinterpret_cast<uintptr_t>(block_ptr);
m_free_regions.subtract({address, address + size});
}
private:
std::mutex m_mutex;
boost::icl::interval_set<uintptr_t> m_free_regions;
};
} // namespace Common

View File

@ -21,18 +21,15 @@
#include <boost/icl/interval_set.hpp> #include <boost/icl/interval_set.hpp>
#include <fcntl.h> #include <fcntl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/random.h>
#include <unistd.h> #include <unistd.h>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#endif // ^^^ Linux ^^^ #endif // ^^^ Linux ^^^
#include <mutex> #include <mutex>
#include <random>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/free_region_manager.h"
#include "common/host_memory.h" #include "common/host_memory.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -144,7 +141,7 @@ public:
Release(); Release();
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { void Map(size_t virtual_offset, size_t host_offset, size_t length) {
std::unique_lock lock{placeholder_mutex}; std::unique_lock lock{placeholder_mutex};
if (!IsNiechePlaceholder(virtual_offset, length)) { if (!IsNiechePlaceholder(virtual_offset, length)) {
Split(virtual_offset, length); Split(virtual_offset, length);
@ -163,7 +160,7 @@ public:
} }
} }
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
DWORD new_flags{}; DWORD new_flags{};
if (read && write) { if (read && write) {
new_flags = PAGE_READWRITE; new_flags = PAGE_READWRITE;
@ -189,11 +186,6 @@ public:
} }
} }
void EnableDirectMappedAddress() {
// TODO
UNREACHABLE();
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
@ -361,55 +353,6 @@ private:
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv #elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
#ifdef ARCHITECTURE_arm64
static void* ChooseVirtualBase(size_t virtual_size) {
constexpr uintptr_t Map39BitSize = (1ULL << 39);
constexpr uintptr_t Map36BitSize = (1ULL << 36);
// This is not a cryptographic application, we just want something random.
std::mt19937_64 rng;
// We want to ensure we are allocating at an address aligned to the L2 block size.
// For Qualcomm devices, we must also allocate memory above 36 bits.
const size_t lower = Map36BitSize / HugePageSize;
const size_t upper = (Map39BitSize - virtual_size) / HugePageSize;
const size_t range = upper - lower;
// Try up to 64 times to allocate memory at random addresses in the range.
for (int i = 0; i < 64; i++) {
// Calculate a possible location.
uintptr_t hint_address = ((rng() % range) + lower) * HugePageSize;
// Try to map.
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer =
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
// If we successfully mapped, we're done.
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
return map_pointer;
}
// Unmap if necessary, and try again.
if (map_pointer != MAP_FAILED) {
munmap(map_pointer, virtual_size);
}
}
return MAP_FAILED;
}
#else
static void* ChooseVirtualBase(size_t virtual_size) {
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
}
#endif
class HostMemory::Impl { class HostMemory::Impl {
public: public:
explicit Impl(size_t backing_size_, size_t virtual_size_) explicit Impl(size_t backing_size_, size_t virtual_size_)
@ -472,7 +415,8 @@ public:
} }
} }
#else #else
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size)); virtual_base = static_cast<u8*>(mmap(nullptr, virtual_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (virtual_base == MAP_FAILED) { if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
throw std::bad_alloc{}; throw std::bad_alloc{};
@ -480,7 +424,7 @@ public:
madvise(virtual_base, virtual_size, MADV_HUGEPAGE); madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
#endif #endif
free_manager.SetAddressSpace(virtual_base, virtual_size); placeholders.add({0, virtual_size});
good = true; good = true;
} }
@ -488,29 +432,14 @@ public:
Release(); Release();
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { void Map(size_t virtual_offset, size_t host_offset, size_t length) {
// Intersect the range with our address space. {
AdjustMap(&virtual_offset, &length); std::scoped_lock lock{placeholder_mutex};
placeholders.subtract({virtual_offset, virtual_offset + length});
// We are removing a placeholder.
free_manager.AllocateBlock(virtual_base + virtual_offset, length);
// Deduce mapping protection flags.
int flags = PROT_NONE;
if (True(perms & MemoryPermission::Read)) {
flags |= PROT_READ;
} }
if (True(perms & MemoryPermission::Write)) {
flags |= PROT_WRITE;
}
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute)) {
flags |= PROT_EXEC;
}
#endif
void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd, void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
host_offset); MAP_SHARED | MAP_FIXED, fd, host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
} }
@ -518,54 +447,47 @@ public:
// The method name is wrong. We're still talking about the virtual range. // The method name is wrong. We're still talking about the virtual range.
// We don't want to unmap, we want to reserve this memory. // We don't want to unmap, we want to reserve this memory.
// Intersect the range with our address space. {
AdjustMap(&virtual_offset, &length); std::scoped_lock lock{placeholder_mutex};
auto it = placeholders.find({virtual_offset - 1, virtual_offset + length + 1});
// Merge with any adjacent placeholder mappings. if (it != placeholders.end()) {
auto [merged_pointer, merged_size] = size_t prev_upper = virtual_offset + length;
free_manager.FreeBlock(virtual_base + virtual_offset, length); virtual_offset = std::min(virtual_offset, it->lower());
length = std::max(it->upper(), prev_upper) - virtual_offset;
}
void* ret = mmap(merged_pointer, merged_size, PROT_NONE, placeholders.add({virtual_offset, virtual_offset + length});
}
void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno)); ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
} }
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) { void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
// Intersect the range with our address space. int flags = 0;
AdjustMap(&virtual_offset, &length);
int flags = PROT_NONE;
if (read) { if (read) {
flags |= PROT_READ; flags |= PROT_READ;
} }
if (write) { if (write) {
flags |= PROT_WRITE; flags |= PROT_WRITE;
} }
#ifdef ARCHITECTURE_arm64
if (execute) {
flags |= PROT_EXEC;
}
#endif
int ret = mprotect(virtual_base + virtual_offset, length, flags); int ret = mprotect(virtual_base + virtual_offset, length, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno)); ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
} }
void EnableDirectMappedAddress() {
virtual_base = nullptr;
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_map_base{reinterpret_cast<u8*>(MAP_FAILED)};
private: private:
/// Release all resources in the object /// Release all resources in the object
void Release() { void Release() {
if (virtual_map_base != MAP_FAILED) { if (virtual_base != MAP_FAILED) {
int ret = munmap(virtual_map_base, virtual_size); int ret = munmap(virtual_base, virtual_size);
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno)); ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
} }
@ -580,29 +502,10 @@ private:
} }
} }
void AdjustMap(size_t* virtual_offset, size_t* length) {
if (virtual_base != nullptr) {
return;
}
// If we are direct mapped, we want to make sure we are operating on a region
// that is in range of our virtual mapping.
size_t intended_start = *virtual_offset;
size_t intended_end = intended_start + *length;
size_t address_space_start = reinterpret_cast<size_t>(virtual_map_base);
size_t address_space_end = address_space_start + virtual_size;
if (address_space_start > intended_end || intended_start > address_space_end) {
*virtual_offset = 0;
*length = 0;
} else {
*virtual_offset = std::max(intended_start, address_space_start);
*length = std::min(intended_end, address_space_end) - *virtual_offset;
}
}
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{};
boost::icl::interval_set<size_t> placeholders; ///< Mapped placeholders
std::mutex placeholder_mutex; ///< Mutex for placeholders
}; };
#else // ^^^ Linux ^^^ vvv Generic vvv #else // ^^^ Linux ^^^ vvv Generic vvv
@ -615,11 +518,11 @@ public:
throw std::bad_alloc{}; throw std::bad_alloc{};
} }
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm) {} void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
void Unmap(size_t virtual_offset, size_t length) {} void Unmap(size_t virtual_offset, size_t length) {}
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {} void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
u8* backing_base{nullptr}; u8* backing_base{nullptr};
u8* virtual_base{nullptr}; u8* virtual_base{nullptr};
@ -632,16 +535,15 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
try { try {
// Try to allocate a fastmem arena. // Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors. // The implementation will fail with std::bad_alloc on errors.
impl = impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) +
AlignUp(virtual_size, PageAlignment) + HugePageSize); 3 * HugePageSize);
backing_base = impl->backing_base; backing_base = impl->backing_base;
virtual_base = impl->virtual_base; virtual_base = impl->virtual_base;
if (virtual_base) { if (virtual_base) {
// Ensure the virtual base is aligned to the L2 block size. virtual_base += 2 * HugePageSize - 1;
virtual_base = reinterpret_cast<u8*>( virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
Common::AlignUp(reinterpret_cast<uintptr_t>(virtual_base), HugePageSize));
virtual_base_offset = virtual_base - impl->virtual_base; virtual_base_offset = virtual_base - impl->virtual_base;
} }
@ -660,8 +562,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
MemoryPermission perms) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
@ -670,7 +571,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
} }
void HostMemory::Unmap(size_t virtual_offset, size_t length) { void HostMemory::Unmap(size_t virtual_offset, size_t length) {
@ -683,22 +584,14 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
impl->Unmap(virtual_offset + virtual_base_offset, length); impl->Unmap(virtual_offset + virtual_base_offset, length);
} }
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
bool execute) {
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
if (length == 0 || !virtual_base || !impl) { if (length == 0 || !virtual_base || !impl) {
return; return;
} }
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
}
void HostMemory::EnableDirectMappedAddress() {
if (impl) {
impl->EnableDirectMappedAddress();
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
}
} }
} // namespace Common } // namespace Common

View File

@ -4,20 +4,11 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
enum class MemoryPermission : u32 {
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,
Execute = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
/** /**
* A low level linear memory buffer, which supports multiple mappings * A low level linear memory buffer, which supports multiple mappings
* Its purpose is to rebuild a given sparse memory layout, including mirrors. * Its purpose is to rebuild a given sparse memory layout, including mirrors.
@ -40,13 +31,11 @@ public:
HostMemory(HostMemory&& other) noexcept; HostMemory(HostMemory&& other) noexcept;
HostMemory& operator=(HostMemory&& other) noexcept; HostMemory& operator=(HostMemory&& other) noexcept;
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); void Map(size_t virtual_offset, size_t host_offset, size_t length);
void Unmap(size_t virtual_offset, size_t length); void Unmap(size_t virtual_offset, size_t length);
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); void Protect(size_t virtual_offset, size_t length, bool read, bool write);
void EnableDirectMappedAddress();
[[nodiscard]] u8* BackingBasePointer() noexcept { [[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base; return backing_base;

View File

@ -9,12 +9,12 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default; PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
Common::ProcessAddress address) const { u64 address) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry->phys_addr = 0; out_entry.phys_addr = 0;
out_entry->block_size = page_size; out_entry.block_size = page_size;
out_context->next_page = 0; out_context.next_page = 0;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = address / page_size; const auto page = address / page_size;
@ -29,20 +29,20 @@ bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_
} }
// Populate the results. // Populate the results.
out_entry->phys_addr = phys_addr + GetInteger(address); out_entry.phys_addr = phys_addr + address;
out_context->next_page = page + 1; out_context.next_page = page + 1;
out_context->next_offset = GetInteger(address) + page_size; out_context.next_offset = address + page_size;
return true; return true;
} }
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
// Setup invalid defaults. // Setup invalid defaults.
out_entry->phys_addr = 0; out_entry.phys_addr = 0;
out_entry->block_size = page_size; out_entry.block_size = page_size;
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = context->next_page; const auto page = context.next_page;
if (page >= backing_addr.size()) { if (page >= backing_addr.size()) {
return false; return false;
} }
@ -54,9 +54,9 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
} }
// Populate the results. // Populate the results.
out_entry->phys_addr = phys_addr + context->next_offset; out_entry.phys_addr = phys_addr + context.next_offset;
context->next_page = page + 1; context.next_page = page + 1;
context->next_offset += page_size; context.next_offset += page_size;
return true; return true;
} }

View File

@ -6,7 +6,6 @@
#include <atomic> #include <atomic>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/typed_address.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
namespace Common { namespace Common {
@ -101,9 +100,9 @@ struct PageTable {
PageTable(PageTable&&) noexcept = default; PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default; PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
Common::ProcessAddress address) const; u64 address) const;
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const; bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
/** /**
* Resizes the page table to be able to accommodate enough pages within * Resizes the page table to be able to accommodate enough pages within
@ -118,16 +117,6 @@ struct PageTable {
return current_address_space_width_in_bits; return current_address_space_width_in_bits;
} }
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
Common::ProcessAddress virt_addr) const {
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
return false;
}
*out_phys_addr = backing_addr[virt_addr / page_size] + GetInteger(virt_addr);
return true;
}
/** /**
* Vector of memory pointers backing each page. An entry can only be non-null if the * Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding attribute element is of type `Memory`. * corresponding attribute element is of type `Memory`.

View File

@ -41,7 +41,6 @@ SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuBackend, true);
SWITCHABLE(CpuAccuracy, true); SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
@ -156,22 +155,6 @@ bool IsFastmemEnabled() {
return true; return true;
} }
static bool is_nce_enabled = false;
void SetNceEnabled(bool is_39bit) {
const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
if (is_nce_selected && !is_nce_enabled) {
LOG_WARNING(
Common,
"Program does not utilize 39-bit address space, unable to natively execute code");
}
}
bool IsNceEnabled() {
return is_nce_enabled;
}
bool IsDockedMode() { bool IsDockedMode() {
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked; return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
} }
@ -220,12 +203,10 @@ 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

@ -63,7 +63,6 @@ SWITCHABLE(AspectRatio, true);
SWITCHABLE(AstcDecodeMode, true); SWITCHABLE(AstcDecodeMode, true);
SWITCHABLE(AstcRecompression, true); SWITCHABLE(AstcRecompression, true);
SWITCHABLE(AudioMode, true); SWITCHABLE(AudioMode, true);
SWITCHABLE(CpuBackend, true);
SWITCHABLE(CpuAccuracy, true); SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
@ -154,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, true, true}; linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, 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};
@ -180,14 +179,6 @@ struct Values {
&use_speed_limit}; &use_speed_limit};
// Cpu // Cpu
SwitchableSetting<CpuBackend, true> cpu_backend{
linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
#ifdef ARCHITECTURE_arm64
CpuBackend::Nce,
#else
CpuBackend::Dynarmic,
#endif
"cpu_backend", Category::Cpu};
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
CpuAccuracy::Auto, CpuAccuracy::Paranoid, CpuAccuracy::Auto, CpuAccuracy::Paranoid,
"cpu_accuracy", Category::Cpu}; "cpu_accuracy", Category::Cpu};
@ -241,11 +232,7 @@ 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",
@ -317,11 +304,7 @@ 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",
@ -330,38 +313,20 @@ struct Values {
true, true,
true}; true};
GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage, SwitchableSetting<AnisotropyMode, true> max_anisotropy{
#ifdef ANDROID linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16,
AnisotropyMode::Default, "max_anisotropy", Category::RendererAdvanced};
#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, SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation",
#ifdef ANDROID Category::RendererAdvanced};
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, SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing",
#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};
@ -393,8 +358,6 @@ 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,
@ -427,11 +390,7 @@ 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,
@ -575,8 +534,6 @@ bool IsGPULevelExtreme();
bool IsGPULevelHigh(); bool IsGPULevelHigh();
bool IsFastmemEnabled(); bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
bool IsDockedMode(); bool IsDockedMode();

View File

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

View File

@ -129,8 +129,6 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV);
ENUM(GpuAccuracy, Normal, High, Extreme); ENUM(GpuAccuracy, Normal, High, Extreme);
ENUM(CpuBackend, Dynarmic, Nce);
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);

View File

@ -6,11 +6,10 @@
namespace Settings { namespace Settings {
namespace NativeButton { namespace NativeButton {
const std::array<const char*, NumButtons> mapping = {{ const std::array<const char*, NumButtons> mapping = {{
"button_a", "button_b", "button_x", "button_y", "button_lstick", "button_a", "button_b", "button_x", "button_y", "button_lstick",
"button_rstick", "button_l", "button_r", "button_zl", "button_zr", "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
"button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot", "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
"button_slright", "button_srright",
}}; }};
} }

View File

@ -29,15 +29,12 @@ enum Values : int {
DRight, DRight,
DDown, DDown,
SLLeft, SL,
SRLeft, SR,
Home, Home,
Screenshot, Screenshot,
SLRight,
SRRight,
NumButtons, NumButtons,
}; };

View File

@ -1,42 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <dlfcn.h>
#include "common/assert.h"
#include "common/dynamic_library.h"
#include "common/scope_exit.h"
#include "common/signal_chain.h"
namespace Common {
template <typename T>
T* LookupLibcSymbol(const char* name) {
#if defined(__BIONIC__)
Common::DynamicLibrary provider("libc.so");
if (!provider.IsOpen()) {
UNREACHABLE_MSG("Failed to open libc!");
}
#else
// For other operating environments, we assume the symbol is not overridden.
const char* base = nullptr;
Common::DynamicLibrary provider(base);
#endif
void* sym = provider.GetSymbolAddress(name);
if (sym == nullptr) {
sym = dlsym(RTLD_DEFAULT, name);
}
if (sym == nullptr) {
UNREACHABLE_MSG("Unable to find symbol {}!", name);
}
return reinterpret_cast<T*>(sym);
}
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
return libc_sigaction(signum, act, oldact);
}
} // namespace Common

View File

@ -1,19 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifndef _WIN32
#include <signal.h>
namespace Common {
// Android's ART overrides sigaction with its own wrapper. This is problematic for SIGSEGV
// in particular, because ART's handler accesses tpidr_el0, which conflicts with NCE.
// This extracts the libc symbol and calls it directly.
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact);
} // namespace Common
#endif

View File

@ -271,9 +271,8 @@ add_library(core STATIC
hle/kernel/k_page_heap.h hle/kernel/k_page_heap.h
hle/kernel/k_page_group.cpp hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h hle/kernel/k_page_table.h
hle/kernel/k_page_table_base.cpp
hle/kernel/k_page_table_base.h
hle/kernel/k_page_table_manager.h hle/kernel/k_page_table_manager.h
hle/kernel/k_page_table_slab_heap.h hle/kernel/k_page_table_slab_heap.h
hle/kernel/k_port.cpp hle/kernel/k_port.cpp
@ -281,7 +280,6 @@ add_library(core STATIC
hle/kernel/k_priority_queue.h hle/kernel/k_priority_queue.h
hle/kernel/k_process.cpp hle/kernel/k_process.cpp
hle/kernel/k_process.h hle/kernel/k_process.h
hle/kernel/k_process_page_table.h
hle/kernel/k_readable_event.cpp hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp hle/kernel/k_resource_limit.cpp
@ -332,6 +330,8 @@ add_library(core STATIC
hle/kernel/physical_core.cpp hle/kernel/physical_core.cpp
hle/kernel/physical_core.h hle/kernel/physical_core.h
hle/kernel/physical_memory.h hle/kernel/physical_memory.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/slab_helpers.h hle/kernel/slab_helpers.h
hle/kernel/svc.cpp hle/kernel/svc.cpp
hle/kernel/svc.h hle/kernel/svc.h
@ -521,28 +521,17 @@ add_library(core STATIC
hle/service/grc/grc.h hle/service/grc/grc.h
hle/service/hid/hid.cpp hle/service/hid/hid.cpp
hle/service/hid/hid.h hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h
hle/service/hid/hid_util.h
hle/service/hid/hidbus.cpp hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h hle/service/hid/hidbus.h
hle/service/hid/irs.cpp hle/service/hid/irs.cpp
hle/service/hid/irs.h hle/service/hid/irs.h
hle/service/hid/irs_ring_lifo.h hle/service/hid/irs_ring_lifo.h
hle/service/hid/resource_manager.cpp
hle/service/hid/resource_manager.h
hle/service/hid/ring_lifo.h hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp hle/service/hid/xcd.cpp
hle/service/hid/xcd.h hle/service/hid/xcd.h
hle/service/hid/errors.h hle/service/hid/errors.h
hle/service/hid/controllers/console_six_axis.cpp hle/service/hid/controllers/console_sixaxis.cpp
hle/service/hid/controllers/console_six_axis.h hle/service/hid/controllers/console_sixaxis.h
hle/service/hid/controllers/controller_base.cpp hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp hle/service/hid/controllers/debug_pad.cpp
@ -557,10 +546,6 @@ add_library(core STATIC
hle/service/hid/controllers/npad.h hle/service/hid/controllers/npad.h
hle/service/hid/controllers/palma.cpp hle/service/hid/controllers/palma.cpp
hle/service/hid/controllers/palma.h hle/service/hid/controllers/palma.h
hle/service/hid/controllers/seven_six_axis.cpp
hle/service/hid/controllers/seven_six_axis.h
hle/service/hid/controllers/six_axis.cpp
hle/service/hid/controllers/six_axis.h
hle/service/hid/controllers/stubbed.cpp hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp hle/service/hid/controllers/touchscreen.cpp
@ -730,7 +715,6 @@ add_library(core STATIC
hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h hle/service/nvnflinger/status.h
hle/service/nvnflinger/ui/fence.h hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h hle/service/nvnflinger/window.h
hle/service/olsc/olsc.cpp hle/service/olsc/olsc.cpp
@ -926,23 +910,6 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service) target_link_libraries(core PRIVATE web_service)
endif() endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
target_compile_definitions(core PRIVATE -DHAS_NCE)
enable_language(C ASM)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
target_sources(core PRIVATE
arm/nce/arm_nce.cpp
arm/nce/arm_nce.h
arm/nce/arm_nce.s
arm/nce/guest_context.h
arm/nce/patch.cpp
arm/nce/patch.h
arm/nce/instructions.h
)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_sources(core PRIVATE target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic.h

View File

@ -153,14 +153,6 @@ 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) {
@ -182,6 +174,14 @@ 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)) {
@ -201,8 +201,6 @@ void ARM_Interface::Run() {
if (True(hr & HaltReason::DataAbort)) { if (True(hr & HaltReason::DataAbort)) {
if (system.DebuggerEnabled()) { if (system.DebuggerEnabled()) {
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint()); system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
} else {
LogBacktrace();
} }
current_thread->RequestSuspend(SuspendType::Debug); current_thread->RequestSuspend(SuspendType::Debug);
break; break;

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