Compare commits

...

53 Commits

Author SHA1 Message Date
0a22e24289 Android #129 2023-11-11 00:57:19 +00:00
2f9487cd38 Merge pull request #11981 from lucasreis1/patch
Allocate resources for test window before getting system info
2023-11-10 10:38:49 -05:00
edce713fc9 Allocate resources for test window before getting system info 2023-11-07 22:47:02 -04:00
f75363177e Merge pull request #11977 from SamayXD/patch-1
Update CMakeLists.txt
2023-11-06 16:46:57 -05:00
4c6217f09b Update CMakeLists.txt 2023-11-07 02:20:29 +05:30
c95f35ea85 Update CMakeLists.txt
Updated Comments for better readability.
2023-11-07 02:13:15 +05:30
40357098a2 Merge pull request #11896 from liamwhite/crop
renderer_vulkan: fix cropping for presentation
2023-11-06 12:08:03 -05:00
e7f4110791 Merge pull request #11873 from liamwhite/buffer-control
nvdrv: add ioctl command serialization wrapper
2023-11-06 12:07:50 -05:00
ca1dd1862b Merge pull request #11972 from t895/fps-counter-adjustments
android: FPS counter adjustments
2023-11-06 11:09:59 -05:00
737d1cea62 Merge pull request #11974 from t895/ci-fix-again
ci: android: Declare secrets during build step
2023-11-06 11:03:19 -05:00
2f8e237ab7 ci: android: Declare secrets during build step 2023-11-05 21:09:21 -05:00
5191465b0a android: Simplify FPS counter padding 2023-11-05 18:34:27 -05:00
50c604f37f android: Color the FPS counter white 2023-11-05 18:29:00 -05:00
dfbc22c291 Merge pull request #11971 from german77/recent
service: am: Set the correct album program id
2023-11-05 18:27:24 -05:00
a5a3167eba service: am: Set the correct album program id 2023-11-05 17:26:34 -06:00
a423e0f9e0 renderer_vulkan: render on bottom of surface clip when flipped (#11894) 2023-11-05 21:47:35 +01:00
511c1f0c8b Merge pull request #11957 from liamwhite/null2
renderer_null: fix
2023-11-05 13:15:19 -05:00
8369fcd71a Merge pull request #11969 from german77/profile
service: acc: Ensure proper profile size
2023-11-05 13:15:14 -05:00
626916e9a4 Merge pull request #11961 from german77/recent
yuzu: Only store games in the recently played list
2023-11-05 13:15:07 -05:00
507f360a81 yuzu: Only store games in the recently played list 2023-11-05 09:34:16 -06:00
5323d9f6b3 service: acc: Ensure proper profile size 2023-11-05 09:28:22 -06:00
770d4b0b72 Merge pull request #11965 from german77/color
core: hid: Signal color updates
2023-11-04 23:26:24 -04:00
e5fed31009 Merge pull request #11963 from Kelebek1/eol_lf
Convert src/ to LF eol
2023-11-04 23:26:09 -04:00
f07484bc64 core: hid: Signal color updates 2023-11-04 14:13:18 -06:00
78b9956a04 Skip git blame 2023-11-04 18:26:49 +00:00
90aa937593 Convert files to LF eol 2023-11-04 18:25:40 +00:00
940618a64d Merge pull request #11952 from liamwhite/opus_stereo_count
opus: Allow 0 stereo count
2023-11-04 11:28:47 -04:00
409fa5dda2 Merge pull request #11960 from german77/silence
service: hid: Silence EnableUnintendedHomeButtonInputProtection
2023-11-04 11:14:27 -04:00
211b67668d Merge pull request #11959 from t895/firmware-reload-fix
android: Don't reload log/system after loading firmware/backup
2023-11-04 11:14:21 -04:00
f0cd02b9bd Merge pull request #11881 from liamwhite/sockets-safe-access
sockets: use safe access helpers
2023-11-04 11:14:08 -04:00
34101d8c5e Merge pull request #11885 from liamwhite/stop-nagging-me
qt: remove duplicate exit confirmation setting
2023-11-04 11:14:01 -04:00
bf8d7bc0da service: hid: Silence EnableUnintendedHomeButtonInputProtection 2023-11-03 23:22:28 -06:00
9543adf072 android: Always update FPS counter 2023-11-04 00:04:20 -04:00
036d2686af android: Don't reload log/system after loading firmware/backup 2023-11-03 22:49:31 -04:00
a80e0e7da5 Merge pull request #11954 from t895/log-hardware
android: Log more system information
2023-11-03 21:16:35 -04:00
9631dedea9 Merge pull request #11955 from t895/cntfrq-fix
arm: NativeClock: Special handling for bad system counter clock frequ…
2023-11-03 21:14:01 -04:00
75de0cadcf renderer_null: fix 2023-11-03 20:54:38 -04:00
4b321c003c arm: NativeClock: Special handling for bad system counter clock frequency reporting
On some devices, checking the system counter clock frequency will return 0. Substitute in the correct values to prevent issues.
2023-11-03 16:21:54 -04:00
0a83047368 android: Log more system information during startup
Logs device manufacturer/model, SoC manufacturer/model where available, and the total system memory
2023-11-03 15:52:01 -04:00
9bb8ac7cb6 android: Fix fetching system memory size from MemoryUtil
We weren't rounding up the value at a unit before (GB, MB, etc) we were rounding up the total bytes and that would do nothing. This fixes that, and the check for total system memory during first emulation start where we tried to check the required system memory against 1 gigabyte.
2023-11-03 15:51:17 -04:00
a294beb116 Allow 0 stereo count 2023-11-03 11:45:40 -04:00
6a7123826a qt: remove duplicate exit confirmation setting 2023-10-31 10:31:50 -04:00
6513a356f0 renderer_vulkan: fix FSR cropping 2023-10-28 11:43:00 -04:00
65d4a16afd renderer_vulkan: fix cropping for presentation 2023-10-28 00:05:06 -04:00
ca75c58f43 sockets: use safe access helpers 2023-10-25 14:07:22 -04:00
723df0f368 nvdrv: rework to remove memcpy 2023-10-25 13:05:56 -04:00
94b7ac50bb nvdrv: fix up remaining copy calls 2023-10-25 13:05:56 -04:00
18450ebd78 nvdrv: convert nvmap 2023-10-25 13:05:56 -04:00
efdb2e8f3d nvdrv: convert codec devices 2023-10-25 13:05:56 -04:00
7a84a1a974 nvdrv: convert nvhost_gpu 2023-10-25 13:05:56 -04:00
789d9c8af9 nvdrv: convert nvhost_ctrl 2023-10-25 13:05:56 -04:00
4df063209b nvdrv: convert nvhost_ctrl_gpu 2023-10-25 13:05:55 -04:00
6256e3ca8e nvdrv: add ioctl command serialization, convert nvhost_as_gpu 2023-10-25 13:05:55 -04:00
72 changed files with 1674 additions and 1551 deletions

5
.git-blame-ignore-revs Normal file
View File

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

View File

@ -1,3 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -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 build with Unicode support. # Ensure that projects are built 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- # Disable module support explicitly due to conflicts with precompiled headers /experimental:module- # Explicitly disable module support 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 turning off warnings for external headers /external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.
# Warnings # Warnings
/W4 /W4

View File

@ -252,7 +252,7 @@ object NativeLibrary {
external fun reloadKeys(): Boolean external fun reloadKeys(): Boolean
external fun initializeSystem() external fun initializeSystem(reload: Boolean)
external fun defaultCPUCore(): Int external fun defaultCPUCore(): Int

View File

@ -11,6 +11,7 @@ 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
@ -49,6 +50,7 @@ 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.Gb)) { if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
Toast.makeText( Toast.makeText(
this, this,
getString( getString(

View File

@ -10,7 +10,6 @@ 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
@ -155,7 +154,6 @@ 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 {
@ -414,12 +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 == true) { if (emulationViewModel.emulationStarted.value) {
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
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", perfStats[FPS])
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
} }
} }
perfStatsUpdateHandler.post(perfStatsUpdater!!) perfStatsUpdateHandler.post(perfStatsUpdater!!)
@ -464,7 +462,6 @@ 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
@ -478,7 +475,6 @@ 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()
@ -486,7 +482,6 @@ 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()
} }
@ -712,24 +707,6 @@ 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
} }
} }

View File

@ -403,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else { } else {
firmwarePath.deleteRecursively() firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true) cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem() NativeLibrary.initializeSystem(true)
getString(R.string.save_file_imported_success) getString(R.string.save_file_imported_success)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -649,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
// Reinitialize relevant data // Reinitialize relevant data
NativeLibrary.initializeSystem() NativeLibrary.initializeSystem(true)
gamesViewModel.reloadGames(false) gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success) return@newInstance getString(R.string.user_data_import_success)

View File

@ -15,7 +15,7 @@ object DirectoryInitialization {
fun start() { fun start() {
if (!areDirectoriesReady) { if (!areDirectoriesReady) {
initializeInternalStorage() initializeInternalStorage()
NativeLibrary.initializeSystem() NativeLibrary.initializeSystem(false)
areDirectoriesReady = true areDirectoriesReady = true
} }
} }

View File

@ -3,6 +3,8 @@
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
@ -16,4 +18,14 @@ 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): String = private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
when { when {
size < Kb -> { size < Kb -> {
context.getString( context.getString(
@ -39,63 +39,59 @@ object MemoryUtil {
size < Mb -> { size < Mb -> {
context.getString( context.getString(
R.string.memory_formatted, R.string.memory_formatted,
(size / Kb).hundredths, if (roundUp) ceil(size / Kb) else (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,
(size / Mb).hundredths, if (roundUp) ceil(size / Mb) else (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,
(size / Gb).hundredths, if (roundUp) ceil(size / Gb) else (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,
(size / Tb).hundredths, if (roundUp) ceil(size / Tb) else (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,
(size / Pb).hundredths, if (roundUp) ceil(size / Pb) else (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,
(size / Eb).hundredths, if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
context.getString(R.string.memory_exabyte) context.getString(R.string.memory_exabyte)
) )
} }
} }
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for val totalMemory: Float
// 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 ceil( return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { memInfo.advertisedMem.toFloat()
memInfo.advertisedMem.toFloat() } else {
} else { memInfo.totalMem.toFloat()
memInfo.totalMem.toFloat() }
}
)
} }
fun isLessThan(minimum: Int, size: Float): Boolean = fun isLessThan(minimum: Int, size: Float): Boolean =
@ -109,5 +105,7 @@ object MemoryUtil {
else -> totalMemory < Kb && totalMemory < minimum else -> totalMemory < Kb && totalMemory < minimum
} }
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) // 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
fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true)
} }

View File

@ -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 { const Core::PerfStatsResults& EmulationSession::PerfStats() {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex); m_perf_stats = m_system.GetAndResetPerfStats();
return m_perf_stats; return m_perf_stats;
} }
@ -247,11 +247,13 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
} }
} }
void EmulationSession::InitializeSystem() { void EmulationSession::InitializeSystem(bool reload) {
// Initialize logging system if (!reload) {
Common::Log::Initialize(); // Initialize logging system
Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::Initialize();
Common::Log::Start(); Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
}
// Initialize filesystem. // Initialize filesystem.
m_system.SetFilesystem(m_vfs); m_system.SetFilesystem(m_vfs);
@ -381,11 +383,6 @@ 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();
}
} }
} }
@ -667,12 +664,15 @@ 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. // Create the default config.ini.
Config{}; Config{};
// Initialize the emulated system. // Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize(); if (!reload) {
EmulationSession::GetInstance().InitializeSystem(); EmulationSession::GetInstance().System().Initialize();
}
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) {

View File

@ -41,9 +41,9 @@ public:
void RunEmulation(); void RunEmulation();
void ShutdownEmulation(); void ShutdownEmulation();
const Core::PerfStatsResults& PerfStats() const; const Core::PerfStatsResults& PerfStats();
void ConfigureFilesystemProvider(const std::string& filepath); void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem(); void InitializeSystem(bool reload);
Core::SystemResultStatus InitializeEmulation(const std::string& filepath); Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
bool IsHandheldOnly(); bool IsHandheldOnly();
@ -80,6 +80,5 @@ 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

@ -134,16 +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">
<TextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.BodyMedium"
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:shadowColor="@android:color/black" android:paddingHorizontal="20dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" android:textSize="12sp"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />

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 sterero_stream_count) { bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_count) {
return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
sterero_stream_count > 0 && sterero_stream_count <= total_stream_count; stereo_stream_count >= 0 && stereo_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 && 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_ERROR(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 && stereo_stream_count > 0 && return total_stream_count > 0 && static_cast<s32>(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

@ -1,6 +1,9 @@
// 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 {
@ -65,7 +68,23 @@ bool NativeClock::IsNative() const {
u64 NativeClock::GetHostCNTFRQ() { u64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0; u64 cntfrq_el0 = 0;
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0)); std::string_view board{""};
#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

@ -96,18 +96,7 @@ void EmulatedController::ReloadFromSettings() {
} }
controller.color_values = {}; controller.color_values = {};
controller.colors_state.fullkey = { ReloadColorsFromSettings();
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.right = {
.body = GetNpadColor(player.body_color_right),
.button = GetNpadColor(player.button_color_right),
};
ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
@ -128,6 +117,30 @@ void EmulatedController::ReloadFromSettings() {
ReloadInput(); ReloadInput();
} }
void EmulatedController::ReloadColorsFromSettings() {
const auto player_index = NpadIdTypeToIndex(npad_id_type);
const auto& player = Settings::values.players.GetValue()[player_index];
// Avoid updating colors if overridden by physical controller
if (controller.color_values[LeftIndex].body != 0 &&
controller.color_values[RightIndex].body != 0) {
return;
}
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.right = {
.body = GetNpadColor(player.body_color_right),
.button = GetNpadColor(player.button_color_right),
};
}
void EmulatedController::LoadDevices() { void EmulatedController::LoadDevices() {
// TODO(german77): Use more buttons to detect the correct device // TODO(german77): Use more buttons to detect the correct device
const auto left_joycon = button_params[Settings::NativeButton::DRight]; const auto left_joycon = button_params[Settings::NativeButton::DRight];

View File

@ -253,6 +253,9 @@ public:
/// Overrides current mapped devices with the stored configuration and reloads all input devices /// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings(); void ReloadFromSettings();
/// Updates current colors with the ones stored in the configuration
void ReloadColorsFromSettings();
/// Saves the current mapped configuration /// Saves the current mapped configuration
void SaveCurrentConfig(); void SaveCurrentConfig();

View File

@ -3,11 +3,13 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/fs/file.h" #include "common/fs/file.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/polyfill_ranges.h" #include "common/polyfill_ranges.h"
#include "common/stb.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/constants.h" #include "core/constants.h"
@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
} }
static constexpr u32 SanitizeJPEGSize(std::size_t size) { static void JPGToMemory(void* context, void* data, int len) {
std::vector<u8>* jpg_image = static_cast<std::vector<u8>*>(context);
unsigned char* jpg = static_cast<unsigned char*>(data);
jpg_image->insert(jpg_image->end(), jpg, jpg + len);
}
static void SanitizeJPEGImageSize(std::vector<u8>& image) {
constexpr std::size_t max_jpeg_image_size = 0x20000; constexpr std::size_t max_jpeg_image_size = 0x20000;
return static_cast<u32>(std::min(size, max_jpeg_image_size)); constexpr int profile_dimensions = 256;
int original_width, original_height, color_channels;
const auto plain_image =
stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
&original_height, &color_channels, STBI_rgb);
// Resize image to match 256*256
if (original_width != profile_dimensions || original_height != profile_dimensions) {
// Use vector instead of array to avoid overflowing the stack
std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
STBIR_FILTER_BOX);
image.clear();
if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
STBI_rgb, out_image.data(), 0)) {
LOG_ERROR(Service_ACC, "Failed to resize the user provided image.");
}
}
image.resize(std::min(image.size(), max_jpeg_image_size));
} }
class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> { class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
@ -339,19 +368,20 @@ protected:
LOG_WARNING(Service_ACC, LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup..."); "Failed to load user provided image! Falling back to built-in backup...");
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
return; return;
} }
const u32 size = SanitizeJPEGSize(image.GetSize()); std::vector<u8> buffer(image.GetSize());
std::vector<u8> buffer(size);
if (image.Read(buffer) != buffer.size()) { if (image.Read(buffer) != buffer.size()) {
LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
} }
SanitizeJPEGImageSize(buffer);
ctx.WriteBuffer(buffer); ctx.WriteBuffer(buffer);
rb.Push<u32>(size); rb.Push(static_cast<u32>(buffer.size()));
} }
void GetImageSize(HLERequestContext& ctx) { void GetImageSize(HLERequestContext& ctx) {
@ -365,10 +395,18 @@ protected:
if (!image.IsOpen()) { if (!image.IsOpen()) {
LOG_WARNING(Service_ACC, LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup..."); "Failed to load user provided image! Falling back to built-in backup...");
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); rb.Push(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
} else { return;
rb.Push(SanitizeJPEGSize(image.GetSize()));
} }
std::vector<u8> buffer(image.GetSize());
if (image.Read(buffer) != buffer.size()) {
LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image.");
}
SanitizeJPEGImageSize(buffer);
rb.Push(static_cast<u32>(buffer.size()));
} }
void Store(HLERequestContext& ctx) { void Store(HLERequestContext& ctx) {

View File

@ -69,6 +69,30 @@ enum class AppletId : u32 {
MyPage = 0x1A, MyPage = 0x1A,
}; };
enum class AppletProgramId : u64 {
QLaunch = 0x0100000000001000ull,
Auth = 0x0100000000001001ull,
Cabinet = 0x0100000000001002ull,
Controller = 0x0100000000001003ull,
DataErase = 0x0100000000001004ull,
Error = 0x0100000000001005ull,
NetConnect = 0x0100000000001006ull,
ProfileSelect = 0x0100000000001007ull,
SoftwareKeyboard = 0x0100000000001008ull,
MiiEdit = 0x0100000000001009ull,
Web = 0x010000000000100Aull,
Shop = 0x010000000000100Bull,
OverlayDisplay = 0x010000000000100Cull,
PhotoViewer = 0x010000000000100Dull,
Settings = 0x010000000000100Eull,
OfflineWeb = 0x010000000000100Full,
LoginShare = 0x0100000000001010ull,
WebAuth = 0x0100000000001011ull,
Starter = 0x0100000000001012ull,
MyPage = 0x0100000000001013ull,
MaxProgramId = 0x0100000000001FFFull,
};
enum class LibraryAppletMode : u32 { enum class LibraryAppletMode : u32 {
AllForeground = 0, AllForeground = 0,
Background = 1, Background = 1,

View File

@ -1353,7 +1353,7 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
struct Parameters { struct Parameters {
bool unintended_home_button_input_protection; bool is_enabled;
INSERT_PADDING_BYTES_NOINIT(3); INSERT_PADDING_BYTES_NOINIT(3);
Core::HID::NpadIdType npad_id; Core::HID::NpadIdType npad_id;
u64 applet_resource_user_id; u64 applet_resource_user_id;
@ -1364,13 +1364,11 @@ void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad); auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
parameters.unintended_home_button_input_protection, parameters.npad_id); parameters.is_enabled, parameters.npad_id);
LOG_WARNING(Service_HID, LOG_DEBUG(Service_HID,
"(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
"applet_resource_user_id={}", parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
parameters.unintended_home_button_input_protection, parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); rb.Push(result);

View File

@ -0,0 +1,159 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/concepts.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia::Devices {
struct IoctlOneArgTraits {
template <typename T, typename R, typename A, typename... B>
static A GetFirstArgImpl(R (T::*)(A, B...));
};
struct IoctlTwoArgTraits {
template <typename T, typename R, typename A, typename B, typename... C>
static A GetFirstArgImpl(R (T::*)(A, B, C...));
template <typename T, typename R, typename A, typename B, typename... C>
static B GetSecondArgImpl(R (T::*)(A, B, C...));
};
struct Null {};
// clang-format off
template <typename FixedArg, typename VarArg, typename InlInVarArg, typename InlOutVarArg, typename F>
NvResult WrapGeneric(F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, std::span<u8> inline_output) {
constexpr bool HasFixedArg = !std::is_same_v<FixedArg, Null>;
constexpr bool HasVarArg = !std::is_same_v<VarArg, Null>;
constexpr bool HasInlInVarArg = !std::is_same_v<InlInVarArg, Null>;
constexpr bool HasInlOutVarArg = !std::is_same_v<InlOutVarArg, Null>;
// Declare the fixed-size input value.
FixedArg fixed{};
size_t var_offset = 0;
if constexpr (HasFixedArg) {
// Read the fixed-size input value.
var_offset = std::min(sizeof(FixedArg), input.size());
if (var_offset > 0) {
std::memcpy(&fixed, input.data(), var_offset);
}
}
// Read the variable-sized inputs.
const size_t num_var_args = HasVarArg ? ((input.size() - var_offset) / sizeof(VarArg)) : 0;
std::vector<VarArg> var_args(num_var_args);
if constexpr (HasVarArg) {
if (num_var_args > 0) {
std::memcpy(var_args.data(), input.data() + var_offset, num_var_args * sizeof(VarArg));
}
}
const size_t num_inl_in_var_args = HasInlInVarArg ? (inline_input.size() / sizeof(InlInVarArg)) : 0;
std::vector<InlInVarArg> inl_in_var_args(num_inl_in_var_args);
if constexpr (HasInlInVarArg) {
if (num_inl_in_var_args > 0) {
std::memcpy(inl_in_var_args.data(), inline_input.data(), num_inl_in_var_args * sizeof(InlInVarArg));
}
}
// Construct inline output data.
const size_t num_inl_out_var_args = HasInlOutVarArg ? (inline_output.size() / sizeof(InlOutVarArg)) : 0;
std::vector<InlOutVarArg> inl_out_var_args(num_inl_out_var_args);
// Perform the call.
NvResult result = callable(fixed, var_args, inl_in_var_args, inl_out_var_args);
// Copy outputs.
if constexpr (HasFixedArg) {
if (output.size() > 0) {
std::memcpy(output.data(), &fixed, std::min(output.size(), sizeof(FixedArg)));
}
}
if constexpr (HasVarArg) {
if (num_var_args > 0 && output.size() > var_offset) {
const size_t max_var_size = output.size() - var_offset;
std::memcpy(output.data() + var_offset, var_args.data(), std::min(max_var_size, num_var_args * sizeof(VarArg)));
}
}
// Copy inline outputs.
if constexpr (HasInlOutVarArg) {
if (num_inl_out_var_args > 0) {
std::memcpy(inline_output.data(), inl_out_var_args.data(), num_inl_out_var_args * sizeof(InlOutVarArg));
}
}
// We're done.
return result;
}
template <typename Self, typename F, typename... Rest>
NvResult WrapFixed(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
using FixedArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>;
const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
return (self->*callable)(fixed, std::forward<Rest>(rest)...);
};
return WrapGeneric<FixedArg, Null, Null, Null>(std::move(Callable), input, {}, output, {});
}
template <typename Self, typename F, typename... Rest>
NvResult WrapFixedInlOut(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output, Rest&&... rest) {
using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
using InlOutVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
return (self->*callable)(fixed, inl_out, std::forward<Rest>(rest)...);
};
return WrapGeneric<FixedArg, Null, Null, InlOutVarArg>(std::move(Callable), input, {}, output, inline_output);
}
template <typename Self, typename F, typename... Rest>
NvResult WrapVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
using VarArg = typename std::remove_reference_t<decltype(IoctlOneArgTraits::GetFirstArgImpl(callable))>::value_type;
const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
return (self->*callable)(var, std::forward<Rest>(rest)...);
};
return WrapGeneric<Null, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
}
template <typename Self, typename F, typename... Rest>
NvResult WrapFixedVariable(Self* self, F&& callable, std::span<const u8> input, std::span<u8> output, Rest&&... rest) {
using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
using VarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
return (self->*callable)(fixed, var, std::forward<Rest>(rest)...);
};
return WrapGeneric<FixedArg, VarArg, Null, Null>(std::move(Callable), input, {}, output, {});
}
template <typename Self, typename F, typename... Rest>
NvResult WrapFixedInlIn(Self* self, F&& callable, std::span<const u8> input, std::span<const u8> inline_input, std::span<u8> output, Rest&&... rest) {
using FixedArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetFirstArgImpl(callable))>;
using InlInVarArg = typename std::remove_reference_t<decltype(IoctlTwoArgTraits::GetSecondArgImpl(callable))>::value_type;
const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult {
return (self->*callable)(fixed, inl_in, std::forward<Rest>(rest)...);
};
return WrapGeneric<FixedArg, Null, InlInVarArg, Null>(std::move(Callable), input, inline_input, output, {});
}
// clang-format on
} // namespace Service::Nvidia::Devices

View File

@ -11,6 +11,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"
@ -33,21 +34,21 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i
case 'A': case 'A':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return BindChannel(input, output); return WrapFixed(this, &nvhost_as_gpu::BindChannel, input, output);
case 0x2: case 0x2:
return AllocateSpace(input, output); return WrapFixed(this, &nvhost_as_gpu::AllocateSpace, input, output);
case 0x3: case 0x3:
return FreeSpace(input, output); return WrapFixed(this, &nvhost_as_gpu::FreeSpace, input, output);
case 0x5: case 0x5:
return UnmapBuffer(input, output); return WrapFixed(this, &nvhost_as_gpu::UnmapBuffer, input, output);
case 0x6: case 0x6:
return MapBufferEx(input, output); return WrapFixed(this, &nvhost_as_gpu::MapBufferEx, input, output);
case 0x8: case 0x8:
return GetVARegions(input, output); return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output);
case 0x9: case 0x9:
return AllocAsEx(input, output); return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output);
case 0x14: case 0x14:
return Remap(input, output); return WrapVariable(this, &nvhost_as_gpu::Remap, input, output);
default: default:
break; break;
} }
@ -72,7 +73,8 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
case 'A': case 'A':
switch (command.cmd) { switch (command.cmd) {
case 0x8: case 0x8:
return GetVARegions(input, output, inline_output); return WrapFixedInlOut(this, &nvhost_as_gpu::GetVARegions3, input, output,
inline_output);
default: default:
break; break;
} }
@ -87,10 +89,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
void nvhost_as_gpu::OnOpen(DeviceFD fd) {} void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
IoctlAllocAsEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
@ -141,10 +140,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> outpu
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags); params.page_size, params.flags);
@ -194,7 +190,6 @@ NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> o
.big_pages = params.page_size != VM::YUZU_PAGESIZE, .big_pages = params.page_size != VM::YUZU_PAGESIZE,
}; };
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
@ -222,10 +217,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
mapping_map.erase(offset); mapping_map.erase(offset);
} }
NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
IoctlFreeSpace params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
params.pages, params.page_size); params.pages, params.page_size);
@ -264,18 +256,11 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> outpu
return NvResult::BadValue; return NvResult::BadValue;
} }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry); LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size());
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
std::scoped_lock lock(mutex);
entries.resize_destructive(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
if (!vm.initialised) { if (!vm.initialised) {
return NvResult::BadValue; return NvResult::BadValue;
@ -317,14 +302,10 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
} }
} }
std::memcpy(output.data(), entries.data(), output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, LOG_DEBUG(Service_NVDRV,
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
", offset={}", ", offset={}",
@ -421,14 +402,10 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> out
mapping_map[params.offset] = mapping; mapping_map[params.offset] = mapping;
} }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
@ -464,9 +441,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> out
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
@ -493,10 +468,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
}; };
} }
NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size); params.buf_size);
@ -508,15 +480,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
GetVARegionsImpl(params); GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions) {
std::span<u8> inline_output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size); params.buf_size);
@ -528,9 +495,10 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> ou
GetVARegionsImpl(params); GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size()); const size_t num_regions = std::min(params.regions.size(), regions.size());
std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion)); for (size_t i = 0; i < num_regions; i++) {
std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion)); regions[i] = params.regions[i];
}
return NvResult::Success; return NvResult::Success;
} }

View File

@ -139,18 +139,17 @@ private:
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
"IoctlGetVaRegions is incorrect size"); "IoctlGetVaRegions is incorrect size");
NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); NvResult AllocAsEx(IoctlAllocAsEx& params);
NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); NvResult AllocateSpace(IoctlAllocSpace& params);
NvResult Remap(std::span<const u8> input, std::span<u8> output); NvResult Remap(std::span<IoctlRemapEntry> params);
NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); NvResult MapBufferEx(IoctlMapBufferEx& params);
NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); NvResult UnmapBuffer(IoctlUnmapBuffer& params);
NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); NvResult FreeSpace(IoctlFreeSpace& params);
NvResult BindChannel(std::span<const u8> input, std::span<u8> output); NvResult BindChannel(IoctlBindChannel& params);
void GetVARegionsImpl(IoctlGetVaRegions& params); void GetVARegionsImpl(IoctlGetVaRegions& params);
NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); NvResult GetVARegions1(IoctlGetVaRegions& params);
NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
std::span<u8> inline_output);
void FreeMappingLocked(u64 offset); void FreeMappingLocked(u64 offset);
@ -213,7 +212,6 @@ private:
bool initialised{}; bool initialised{};
} vm; } vm;
std::shared_ptr<Tegra::MemoryManager> gmmu; std::shared_ptr<Tegra::MemoryManager> gmmu;
Common::ScratchBuffer<IoctlRemapEntry> entries;
// s32 channel{}; // s32 channel{};
// u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};

View File

@ -14,6 +14,7 @@
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
@ -40,19 +41,19 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x1b: case 0x1b:
return NvOsGetConfigU32(input, output); return WrapFixed(this, &nvhost_ctrl::NvOsGetConfigU32, input, output);
case 0x1c: case 0x1c:
return IocCtrlClearEventWait(input, output); return WrapFixed(this, &nvhost_ctrl::IocCtrlClearEventWait, input, output);
case 0x1d: case 0x1d:
return IocCtrlEventWait(input, output, true); return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, true);
case 0x1e: case 0x1e:
return IocCtrlEventWait(input, output, false); return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, false);
case 0x1f: case 0x1f:
return IocCtrlEventRegister(input, output); return WrapFixed(this, &nvhost_ctrl::IocCtrlEventRegister, input, output);
case 0x20: case 0x20:
return IocCtrlEventUnregister(input, output); return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregister, input, output);
case 0x21: case 0x21:
return IocCtrlEventUnregisterBatch(input, output); return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregisterBatch, input, output);
} }
break; break;
default: default:
@ -79,25 +80,19 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl::NvOsGetConfigU32(IocGetConfigParams& params) {
IocGetConfigParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
params.param_str.data()); params.param_str.data());
return NvResult::ConfigVarNotFound; // Returns error on production mode return NvResult::ConfigVarNotFound; // Returns error on production mode
} }
NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation) {
bool is_allocation) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
params.fence.id, params.fence.value, params.timeout, is_allocation); params.fence.id, params.fence.value, params.timeout, is_allocation);
bool must_unmark_fail = !is_allocation; bool must_unmark_fail = !is_allocation;
const u32 event_id = params.value.raw; const u32 event_id = params.value.raw;
SCOPE_EXIT({ SCOPE_EXIT({
std::memcpy(output.data(), &params, sizeof(params));
if (must_unmark_fail) { if (must_unmark_fail) {
events[event_id].fails = 0; events[event_id].fails = 0;
} }
@ -231,9 +226,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id; const u32 event_id = params.user_event_id;
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
if (event_id >= MaxNvEvents) { if (event_id >= MaxNvEvents) {
@ -252,9 +245,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params) {
IocCtrlEventUnregisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF; const u32 event_id = params.user_event_id & 0x00FF;
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
@ -262,9 +253,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::spa
return FreeEvent(event_id); return FreeEvent(event_id);
} }
NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params) {
IocCtrlEventUnregisterBatchParams params{};
std::memcpy(&params, input.data(), sizeof(params));
u64 event_mask = params.user_events; u64 event_mask = params.user_events;
LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
@ -280,10 +269,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) {
IocCtrlEventClearParams params{};
std::memcpy(&params, input.data(), sizeof(params));
u32 event_id = params.event_id.slot; u32 event_id = params.event_id.slot;
LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);

View File

@ -186,12 +186,12 @@ private:
static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
"IocCtrlEventKill is incorrect size"); "IocCtrlEventKill is incorrect size");
NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); NvResult NvOsGetConfigU32(IocGetConfigParams& params);
NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); NvResult IocCtrlEventRegister(IocCtrlEventRegisterParams& params);
NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); NvResult IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params);
NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); NvResult IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params);
NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); NvResult IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation);
NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output); NvResult IocCtrlClearEventWait(IocCtrlEventClearParams& params);
NvResult FreeEvent(u32 slot); NvResult FreeEvent(u32 slot);

View File

@ -6,6 +6,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"
@ -27,23 +28,23 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
case 'G': case 'G':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return ZCullGetCtxSize(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetCtxSize, input, output);
case 0x2: case 0x2:
return ZCullGetInfo(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetInfo, input, output);
case 0x3: case 0x3:
return ZBCSetTable(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output);
case 0x4: case 0x4:
return ZBCQueryTable(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output);
case 0x5: case 0x5:
return GetCharacteristics(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output);
case 0x6: case 0x6:
return GetTPCMasks(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::GetTPCMasks1, input, output);
case 0x7: case 0x7:
return FlushL2(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output);
case 0x14: case 0x14:
return GetActiveSlotMask(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output);
case 0x1c: case 0x1c:
return GetGpuTime(input, output); return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output);
default: default:
break; break;
} }
@ -65,9 +66,11 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
case 'G': case 'G':
switch (command.cmd) { switch (command.cmd) {
case 0x5: case 0x5:
return GetCharacteristics(input, output, inline_output); return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output,
inline_output);
case 0x6: case 0x6:
return GetTPCMasks(input, output, inline_output); return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetTPCMasks3, input, output,
inline_output);
default: default:
break; break;
} }
@ -82,10 +85,8 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
params.gc.arch = 0x120; params.gc.arch = 0x120;
params.gc.impl = 0xb; params.gc.impl = 0xb;
params.gc.rev = 0xa1; params.gc.rev = 0xa1;
@ -123,15 +124,13 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
params.gc.gr_compbit_store_base_hw = 0x0; params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0; params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, NvResult nvhost_ctrl_gpu::GetCharacteristics3(
std::span<u8> inline_output) { IoctlCharacteristics& params, std::span<IoctlGpuCharacteristics> gpu_characteristics) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
params.gc.arch = 0x120; params.gc.arch = 0x120;
params.gc.impl = 0xb; params.gc.impl = 0xb;
params.gc.rev = 0xa1; params.gc.rev = 0xa1;
@ -169,70 +168,47 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::spa
params.gc.gr_compbit_store_base_hw = 0x0; params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0; params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
if (!gpu_characteristics.empty()) {
std::memcpy(output.data(), &params, output.size()); gpu_characteristics.front() = params.gc;
std::memcpy(inline_output.data(), &params.gc, inline_output.size()); }
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) { if (params.mask_buffer_size != 0) {
params.tcp_mask = 3; params.tcp_mask = 3;
} }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
std::span<u8> inline_output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) { if (params.mask_buffer_size != 0) {
params.tcp_mask = 3; params.tcp_mask = 3;
} }
std::memcpy(output.data(), &params, output.size()); if (!tpc_mask.empty()) {
std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size()); tpc_mask.front() = params.tcp_mask;
}
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlActiveSlotMask params{};
if (input.size() > 0) {
std::memcpy(&params, input.data(), input.size());
}
params.slot = 0x07; params.slot = 0x07;
params.mask = 0x01; params.mask = 0x01;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlZcullGetCtxSize params{};
if (input.size() > 0) {
std::memcpy(&params, input.data(), input.size());
}
params.size = 0x1; params.size = 0x1;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlNvgpuGpuZcullGetInfoArgs params{};
if (input.size() > 0) {
std::memcpy(&params, input.data(), input.size());
}
params.width_align_pixels = 0x20; params.width_align_pixels = 0x20;
params.height_align_pixels = 0x20; params.height_align_pixels = 0x20;
params.pixel_squares_by_aliquots = 0x400; params.pixel_squares_by_aliquots = 0x400;
@ -243,53 +219,28 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8>
params.subregion_width_align_pixels = 0x20; params.subregion_width_align_pixels = 0x20;
params.subregion_height_align_pixels = 0x40; params.subregion_height_align_pixels = 0x40;
params.subregion_count = 0x10; params.subregion_count = 0x10;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called"); LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcSetTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO(ogniK): What does this even actually do? // TODO(ogniK): What does this even actually do?
// Prevent null pointer being passed as arg 1
if (output.empty()) {
LOG_WARNING(Service_NVDRV, "Avoiding passing null pointer to memcpy");
} else {
std::memcpy(output.data(), &params, output.size());
}
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called"); LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called"); LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_ctrl_gpu::GetGpuTime(IoctlGetGpuTime& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }

View File

@ -151,21 +151,20 @@ private:
}; };
static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); NvResult GetCharacteristics1(IoctlCharacteristics& params);
NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, NvResult GetCharacteristics3(IoctlCharacteristics& params,
std::span<u8> inline_output); std::span<IoctlGpuCharacteristics> gpu_characteristics);
NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); NvResult GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params);
NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask);
std::span<u8> inline_output);
NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); NvResult GetActiveSlotMask(IoctlActiveSlotMask& params);
NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params);
NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params);
NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); NvResult ZBCSetTable(IoctlZbcSetTable& params);
NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); NvResult ZBCQueryTable(IoctlZbcQueryTable& params);
NvResult FlushL2(std::span<const u8> input, std::span<u8> output); NvResult FlushL2(IoctlFlushL2& params);
NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output); NvResult GetGpuTime(IoctlGetGpuTime& params);
EventInterface& events_interface; EventInterface& events_interface;

View File

@ -8,6 +8,7 @@
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"
#include "core/memory.h" #include "core/memory.h"
@ -52,7 +53,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x3: case 0x3:
return GetWaitbase(input, output); return WrapFixed(this, &nvhost_gpu::GetWaitbase, input, output);
default: default:
break; break;
} }
@ -60,25 +61,25 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 'H': case 'H':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return SetNVMAPfd(input, output); return WrapFixed(this, &nvhost_gpu::SetNVMAPfd, input, output);
case 0x3: case 0x3:
return ChannelSetTimeout(input, output); return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
case 0x8: case 0x8:
return SubmitGPFIFOBase(input, output, false); return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
case 0x9: case 0x9:
return AllocateObjectContext(input, output); return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
case 0xb: case 0xb:
return ZCullBind(input, output); return WrapFixed(this, &nvhost_gpu::ZCullBind, input, output);
case 0xc: case 0xc:
return SetErrorNotifier(input, output); return WrapFixed(this, &nvhost_gpu::SetErrorNotifier, input, output);
case 0xd: case 0xd:
return SetChannelPriority(input, output); return WrapFixed(this, &nvhost_gpu::SetChannelPriority, input, output);
case 0x1a: case 0x1a:
return AllocGPFIFOEx2(input, output); return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output);
case 0x1b: case 0x1b:
return SubmitGPFIFOBase(input, output, true); return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
case 0x1d: case 0x1d:
return ChannelSetTimeslice(input, output); return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
default: default:
break; break;
} }
@ -86,9 +87,9 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 'G': case 'G':
switch (command.cmd) { switch (command.cmd) {
case 0x14: case 0x14:
return SetClientData(input, output); return WrapFixed(this, &nvhost_gpu::SetClientData, input, output);
case 0x15: case 0x15:
return GetClientData(input, output); return WrapFixed(this, &nvhost_gpu::GetClientData, input, output);
default: default:
break; break;
} }
@ -104,7 +105,8 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 'H': case 'H':
switch (command.cmd) { switch (command.cmd) {
case 0x1b: case 0x1b:
return SubmitGPFIFOBase(input, inline_input, output); return WrapFixedInlIn(this, &nvhost_gpu::SubmitGPFIFOBase2, input, inline_input,
output);
} }
break; break;
} }
@ -121,63 +123,45 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
void nvhost_gpu::OnOpen(DeviceFD fd) {} void nvhost_gpu::OnOpen(DeviceFD fd) {}
void nvhost_gpu::OnClose(DeviceFD fd) {} void nvhost_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd; nvmap_fd = params.nvmap_fd;
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::SetClientData(IoctlClientData& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
user_data = params.data; user_data = params.data;
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::GetClientData(IoctlClientData& params) {
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
params.data = user_data; params.data = user_data;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::ZCullBind(IoctlZCullBind& params) {
std::memcpy(&zcull_params, input.data(), input.size()); zcull_params = params;
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode); zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::SetErrorNotifier(IoctlSetErrorNotifier& params) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
params.size, params.mem); params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::SetChannelPriority(IoctlChannelSetPriority& params) {
std::memcpy(&channel_priority, input.data(), input.size()); channel_priority = params.priority;
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params) {
IoctlAllocGpfifoEx2 params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, LOG_WARNING(Service_NVDRV,
"(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, " "(STUBBED) called, num_entries={:X}, flags={:X}, unk0={:X}, "
"unk1={:X}, unk2={:X}, unk3={:X}", "unk1={:X}, unk2={:X}, unk3={:X}",
@ -193,18 +177,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> out
params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
IoctlAllocObjCtx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
params.flags); params.flags);
params.obj_id = 0x0; params.obj_id = 0x0;
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
@ -248,8 +228,7 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementW
return result; return result;
} }
NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output, NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries) {
Tegra::CommandList&& entries) {
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw); params.num_entries, params.flags.raw);
@ -290,65 +269,55 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> o
flags.raw = 0; flags.raw = 0;
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
bool kickoff) { std::span<Tegra::CommandListHeader> commands, bool kickoff) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) { if (params.num_entries > commands.size()) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return NvResult::InvalidSize; return NvResult::InvalidSize;
} }
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
Tegra::CommandList entries(params.num_entries);
Tegra::CommandList entries(params.num_entries);
if (kickoff) { if (kickoff) {
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
params.num_entries * sizeof(Tegra::CommandListHeader)); params.num_entries * sizeof(Tegra::CommandListHeader));
} else { } else {
std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], std::memcpy(entries.command_lists.data(), commands.data(),
params.num_entries * sizeof(Tegra::CommandListHeader)); params.num_entries * sizeof(Tegra::CommandListHeader));
} }
return SubmitGPFIFOImpl(params, output, std::move(entries)); return SubmitGPFIFOImpl(params, std::move(entries));
} }
NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
std::span<u8> output) { std::span<const Tegra::CommandListHeader> commands) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) { if (params.num_entries > commands.size()) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return NvResult::InvalidSize; return NvResult::InvalidSize;
} }
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
Tegra::CommandList entries(params.num_entries); Tegra::CommandList entries(params.num_entries);
std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size()); std::memcpy(entries.command_lists.data(), commands.data(),
return SubmitGPFIFOImpl(params, output, std::move(entries)); params.num_entries * sizeof(Tegra::CommandListHeader));
return SubmitGPFIFOImpl(params, std::move(entries));
} }
NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0 params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
IoctlSetTimeslice params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
channel_timeslice = params.timeslice; channel_timeslice = params.timeslice;

View File

@ -186,23 +186,24 @@ private:
u32_le channel_priority{}; u32_le channel_priority{};
u32_le channel_timeslice{}; u32_le channel_timeslice{};
NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
NvResult SetClientData(std::span<const u8> input, std::span<u8> output); NvResult SetClientData(IoctlClientData& params);
NvResult GetClientData(std::span<const u8> input, std::span<u8> output); NvResult GetClientData(IoctlClientData& params);
NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); NvResult ZCullBind(IoctlZCullBind& params);
NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); NvResult SetErrorNotifier(IoctlSetErrorNotifier& params);
NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); NvResult SetChannelPriority(IoctlChannelSetPriority& params);
NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params);
NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); NvResult AllocateObjectContext(IoctlAllocObjCtx& params);
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
Tegra::CommandList&& entries); NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output, NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
bool kickoff = false); std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
std::span<u8> output); std::span<const Tegra::CommandListHeader> commands);
NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); NvResult GetWaitbase(IoctlGetWaitbase& params);
NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output); NvResult ChannelSetTimeout(IoctlChannelSetTimeout& params);
NvResult ChannelSetTimeslice(IoctlSetTimeslice& params);
EventInterface& events_interface; EventInterface& events_interface;
NvCore::Container& core; NvCore::Container& core;

View File

@ -6,6 +6,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -25,18 +26,18 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
if (!host1x_file.fd_to_id.contains(fd)) { if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
} }
return Submit(fd, input, output); return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
} }
case 0x2: case 0x2:
return GetSyncpoint(input, output); return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
case 0x3: case 0x3:
return GetWaitbase(input, output); return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output);
case 0x7: case 0x7:
return SetSubmitTimeout(input, output); return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
case 0x9: case 0x9:
return MapBuffer(input, output); return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output);
case 0xa: case 0xa:
return UnmapBuffer(input, output); return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
default: default:
break; break;
} }
@ -44,7 +45,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
case 'H': case 'H':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return SetNVMAPfd(input); return WrapFixed(this, &nvhost_nvdec::SetNVMAPfd, input, output);
default: default:
break; break;
} }

View File

@ -29,6 +29,9 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si
return 0; return 0;
} }
const size_t bytes_copied = count * sizeof(T); const size_t bytes_copied = count * sizeof(T);
if (input.size() < offset + bytes_copied) {
return 0;
}
std::memcpy(dst.data(), input.data() + offset, bytes_copied); std::memcpy(dst.data(), input.data() + offset, bytes_copied);
return bytes_copied; return bytes_copied;
} }
@ -41,6 +44,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
return 0; return 0;
} }
const size_t bytes_copied = src.size() * sizeof(T); const size_t bytes_copied = src.size() * sizeof(T);
if (dst.size() < offset + bytes_copied) {
return 0;
}
std::memcpy(dst.data() + offset, src.data(), bytes_copied); std::memcpy(dst.data() + offset, src.data(), bytes_copied);
return bytes_copied; return bytes_copied;
} }
@ -63,18 +69,14 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
} }
NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) { NvResult nvhost_nvdec_common::SetNVMAPfd(IoctlSetNvmapFD& params) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd; nvmap_fd = params.nvmap_fd;
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, DeviceFD fd) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
// Instantiate param buffers // Instantiate param buffers
@ -85,12 +87,12 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
std::vector<u32> fence_thresholds(params.fence_count); std::vector<u32> fence_thresholds(params.fence_count);
// Slice input into their respective buffers // Slice input into their respective buffers
std::size_t offset = sizeof(IoctlSubmit); std::size_t offset = 0;
offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset); offset += SliceVectors(data, command_buffers, params.cmd_buffer_count, offset);
offset += SliceVectors(input, relocs, params.relocation_count, offset); offset += SliceVectors(data, relocs, params.relocation_count, offset);
offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset); offset += SliceVectors(data, reloc_shifts, params.relocation_count, offset);
offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset); offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
offset += SliceVectors(input, fence_thresholds, params.fence_count, offset); offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU(); auto& gpu = system.GPU();
if (gpu.UseNvdec()) { if (gpu.UseNvdec()) {
@ -108,72 +110,51 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std
cmdlist.size() * sizeof(u32)); cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
} }
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back // Some games expect command_buffers to be written back
offset = sizeof(IoctlSubmit); offset = 0;
offset += WriteVectors(output, command_buffers, offset); offset += WriteVectors(data, command_buffers, offset);
offset += WriteVectors(output, relocs, offset); offset += WriteVectors(data, relocs, offset);
offset += WriteVectors(output, reloc_shifts, offset); offset += WriteVectors(data, reloc_shifts, offset);
offset += WriteVectors(output, syncpt_increments, offset); offset += WriteVectors(data, syncpt_increments, offset);
offset += WriteVectors(output, fence_thresholds, offset); offset += WriteVectors(data, fence_thresholds, offset);
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvdec_common::GetSyncpoint(IoctlGetSyncpoint& params) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
// const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
params.value = channel_syncpoint; params.value = channel_syncpoint;
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) {
IoctlGetWaitbase params{};
LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
params.value = 0; // Seems to be hard coded at 0 params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) {
IoctlMapBuffer params{}; const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); for (size_t i = 0; i < num_entries; i++) {
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); entries[i].map_address = nvmap.PinHandle(entries[i].map_handle);
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
for (auto& cmd_buffer : cmd_buffer_handles) {
cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
}
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
cmd_buffer_handles.size() * sizeof(MapBufferEntry));
return NvResult::Success;
}
NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
for (auto& cmd_buffer : cmd_buffer_handles) {
nvmap.UnpinHandle(cmd_buffer.map_handle);
} }
std::memset(output.data(), 0, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvdec_common::UnmapBuffer(IoctlMapBuffer& params,
std::memcpy(&submit_timeout, input.data(), input.size()); std::span<MapBufferEntry> entries) {
const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
for (size_t i = 0; i < num_entries; i++) {
nvmap.UnpinHandle(entries[i].map_handle);
entries[i] = {};
}
params = {};
return NvResult::Success;
}
NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called"); LOG_WARNING(Service_NVDRV, "(STUBBED) called");
return NvResult::Success; return NvResult::Success;
} }

View File

@ -107,13 +107,13 @@ protected:
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
/// Ioctl command implementations /// Ioctl command implementations
NvResult SetNVMAPfd(std::span<const u8> input); NvResult SetNVMAPfd(IoctlSetNvmapFD&);
NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); NvResult GetSyncpoint(IoctlGetSyncpoint& params);
NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); NvResult GetWaitbase(IoctlGetWaitbase& params);
NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output); NvResult SetSubmitTimeout(u32 timeout);
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
@ -18,7 +19,7 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
case 'H': case 'H':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return SetNVMAPfd(input, output); return WrapFixed(this, &nvhost_nvjpg::SetNVMAPfd, input, output);
default: default:
break; break;
} }
@ -46,9 +47,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
void nvhost_nvjpg::OnOpen(DeviceFD fd) {} void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
void nvhost_nvjpg::OnClose(DeviceFD fd) {} void nvhost_nvjpg::OnClose(DeviceFD fd) {}
NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) { NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd; nvmap_fd = params.nvmap_fd;

View File

@ -33,7 +33,7 @@ private:
s32_le nvmap_fd{}; s32_le nvmap_fd{};
NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); NvResult SetNVMAPfd(IoctlSetNvmapFD& params);
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -5,6 +5,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -25,16 +26,16 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
if (!host1x_file.fd_to_id.contains(fd)) { if (!host1x_file.fd_to_id.contains(fd)) {
host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
} }
return Submit(fd, input, output); return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
} }
case 0x2: case 0x2:
return GetSyncpoint(input, output); return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
case 0x3: case 0x3:
return GetWaitbase(input, output); return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output);
case 0x9: case 0x9:
return MapBuffer(input, output); return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output);
case 0xa: case 0xa:
return UnmapBuffer(input, output); return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output);
default: default:
break; break;
} }
@ -42,7 +43,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
case 'H': case 'H':
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return SetNVMAPfd(input); return WrapFixed(this, &nvhost_vic::SetNVMAPfd, input, output);
default: default:
break; break;
} }

View File

@ -13,6 +13,7 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/memory.h" #include "core/memory.h"
@ -31,17 +32,17 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
case 0x1: case 0x1:
switch (command.cmd) { switch (command.cmd) {
case 0x1: case 0x1:
return IocCreate(input, output); return WrapFixed(this, &nvmap::IocCreate, input, output);
case 0x3: case 0x3:
return IocFromId(input, output); return WrapFixed(this, &nvmap::IocFromId, input, output);
case 0x4: case 0x4:
return IocAlloc(input, output); return WrapFixed(this, &nvmap::IocAlloc, input, output);
case 0x5: case 0x5:
return IocFree(input, output); return WrapFixed(this, &nvmap::IocFree, input, output);
case 0x9: case 0x9:
return IocParam(input, output); return WrapFixed(this, &nvmap::IocParam, input, output);
case 0xe: case 0xe:
return IocGetId(input, output); return WrapFixed(this, &nvmap::IocGetId, input, output);
default: default:
break; break;
} }
@ -69,9 +70,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st
void nvmap::OnOpen(DeviceFD fd) {} void nvmap::OnOpen(DeviceFD fd) {}
void nvmap::OnClose(DeviceFD fd) {} void nvmap::OnClose(DeviceFD fd) {}
NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocCreate(IocCreateParams& params) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
@ -85,13 +84,10 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
params.handle = handle_description->id; params.handle = handle_description->id;
LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocAlloc(IocAllocParams& params) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
if (!params.handle) { if (!params.handle) {
@ -133,14 +129,10 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
handle_description->size, handle_description->size,
Kernel::KMemoryPermission::None, true, false) Kernel::KMemoryPermission::None, true, false)
.IsSuccess()); .IsSuccess());
std::memcpy(output.data(), &params, sizeof(params));
return result; return result;
} }
NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocGetId(IocGetIdParams& params) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
// See the comment in FromId for extra info on this function // See the comment in FromId for extra info on this function
@ -157,14 +149,10 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
} }
params.id = handle_description->id; params.id = handle_description->id;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocFromId(IocFromIdParams& params) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
// Handles and IDs are always the same value in nvmap however IDs can be used globally given the // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
@ -188,16 +176,12 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
return result; return result;
} }
params.handle = handle_description->id; params.handle = handle_description->id;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocParam(IocParamParams& params) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called type={}", params.param); LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
if (!params.handle) { if (!params.handle) {
@ -237,14 +221,10 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
return NvResult::BadValue; return NvResult::BadValue;
} }
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { NvResult nvmap::IocFree(IocFreeParams& params) {
IocFreeParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called"); LOG_DEBUG(Service_NVDRV, "called");
if (!params.handle) { if (!params.handle) {
@ -267,7 +247,6 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
// This is possible when there's internal dups or other duplicates. // This is possible when there's internal dups or other duplicates.
} }
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }

View File

@ -99,12 +99,12 @@ public:
}; };
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
NvResult IocCreate(std::span<const u8> input, std::span<u8> output); NvResult IocCreate(IocCreateParams& params);
NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); NvResult IocAlloc(IocAllocParams& params);
NvResult IocGetId(std::span<const u8> input, std::span<u8> output); NvResult IocGetId(IocGetIdParams& params);
NvResult IocFromId(std::span<const u8> input, std::span<u8> output); NvResult IocFromId(IocFromIdParams& params);
NvResult IocParam(std::span<const u8> input, std::span<u8> output); NvResult IocParam(IocParamParams& params);
NvResult IocFree(std::span<const u8> input, std::span<u8> output); NvResult IocFree(IocFreeParams& params);
private: private:
/// Id to use for the next handle that is created. /// Id to use for the next handle that is created.

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace Service::android { namespace Service::android {
@ -21,5 +22,6 @@ enum class BufferTransformFlags : u32 {
/// Rotate source image 270 degrees clockwise /// Rotate source image 270 degrees clockwise
Rotate270 = 0x07, Rotate270 = 0x07,
}; };
DECLARE_ENUM_FLAG_OPERATORS(BufferTransformFlags);
} // namespace Service::android } // namespace Service::android

View File

@ -71,24 +71,17 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
R_SUCCEED(); R_SUCCEED();
} }
template <typename T>
std::span<u8> SerializeIoc(T& params) {
return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T));
}
Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) { Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
// Create a handle. // Create a handle.
Nvidia::Devices::nvmap::IocCreateParams create_in_params{ Nvidia::Devices::nvmap::IocCreateParams create_params{
.size = size, .size = size,
.handle = 0, .handle = 0,
}; };
Nvidia::Devices::nvmap::IocCreateParams create_out_params{}; R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success,
R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) ==
Nvidia::NvResult::Success,
VI::ResultOperationFailed); VI::ResultOperationFailed);
// Assign the output handle. // Assign the output handle.
*out_nv_map_handle = create_out_params.handle; *out_nv_map_handle = create_params.handle;
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
@ -96,13 +89,10 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap,
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
// Free the handle. // Free the handle.
Nvidia::Devices::nvmap::IocFreeParams free_in_params{ Nvidia::Devices::nvmap::IocFreeParams free_params{
.handle = handle, .handle = handle,
}; };
Nvidia::Devices::nvmap::IocFreeParams free_out_params{}; R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) ==
Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
@ -111,7 +101,7 @@ Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
u32 size) { u32 size) {
// Assign the allocated memory to the handle. // Assign the allocated memory to the handle.
Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{ Nvidia::Devices::nvmap::IocAllocParams alloc_params{
.handle = handle, .handle = handle,
.heap_mask = 0, .heap_mask = 0,
.flags = {}, .flags = {},
@ -119,10 +109,7 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
.kind = 0, .kind = 0,
.address = GetInteger(buffer), .address = GetInteger(buffer),
}; };
Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{}; R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed);
R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) ==
Nvidia::NvResult::Success,
VI::ResultOperationFailed);
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();

View File

@ -39,6 +39,18 @@ bool IsConnectionBased(Type type) {
} }
} }
template <typename T>
T GetValue(std::span<const u8> buffer) {
T t{};
std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size()));
return t;
}
template <typename T>
void PutValue(std::span<u8> buffer, const T& t) {
std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size()));
}
} // Anonymous namespace } // Anonymous namespace
void BSD::PollWork::Execute(BSD* bsd) { void BSD::PollWork::Execute(BSD* bsd) {
@ -316,22 +328,12 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>(); const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>()); const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
const auto buffer = ctx.ReadBuffer();
const u8* optval = buffer.empty() ? nullptr : buffer.data();
size_t optlen = buffer.size();
std::array<u64, 2> values;
if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
std::memcpy(values.data(), buffer.data(), sizeof(values));
optlen = sizeof(values);
optval = reinterpret_cast<const u8*>(values.data());
}
LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
static_cast<u32>(optname), optlen); static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
} }
void BSD::Shutdown(HLERequestContext& ctx) { void BSD::Shutdown(HLERequestContext& ctx) {
@ -521,18 +523,19 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) { s32 nfds, s32 timeout) {
if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Errno::SUCCESS};
}
if (read_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
if (write_buffer.size() < nfds * sizeof(PollFD)) { if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL}; return {-1, Errno::INVAL};
} }
if (nfds == 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Errno::SUCCESS};
}
const size_t length = std::min(read_buffer.size(), write_buffer.size());
std::vector<PollFD> fds(nfds); std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), length); std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
if (timeout >= 0) { if (timeout >= 0) {
const s64 seconds = timeout / 1000; const s64 seconds = timeout / 1000;
@ -580,7 +583,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con
for (size_t i = 0; i < num; ++i) { for (size_t i = 0; i < num; ++i) {
fds[i].revents = Translate(host_pollfds[i].revents); fds[i].revents = Translate(host_pollfds[i].revents);
} }
std::memcpy(write_buffer.data(), fds.data(), length); std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
return Translate(result); return Translate(result);
} }
@ -608,8 +611,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
new_descriptor.is_connection_based = descriptor.is_connection_based; new_descriptor.is_connection_based = descriptor.is_connection_based;
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size()); PutValue(write_buffer, guest_addr_in);
std::memcpy(write_buffer.data(), &guest_addr_in, length);
return {new_fd, Errno::SUCCESS}; return {new_fd, Errno::SUCCESS};
} }
@ -619,8 +621,7 @@ Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
return Errno::BADF; return Errno::BADF;
} }
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() == sizeof(SockAddrIn));
SockAddrIn addr_in; auto addr_in = GetValue<SockAddrIn>(addr);
std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
} }
@ -631,8 +632,7 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
} }
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
SockAddrIn addr_in; auto addr_in = GetValue<SockAddrIn>(addr);
std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
} }
@ -650,7 +650,7 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
ASSERT(write_buffer.size() >= sizeof(guest_addrin)); ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin));
std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno); return Translate(bsd_errno);
} }
@ -667,7 +667,7 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
ASSERT(write_buffer.size() >= sizeof(guest_addrin)); ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin)); write_buffer.resize(sizeof(guest_addrin));
std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno); return Translate(bsd_errno);
} }
@ -725,7 +725,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
optval.size() == sizeof(Errno), { return Errno::INVAL; }, optval.size() == sizeof(Errno), { return Errno::INVAL; },
"Incorrect getsockopt option size"); "Incorrect getsockopt option size");
optval.resize(sizeof(Errno)); optval.resize(sizeof(Errno));
memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); PutValue(optval, translated_pending_err);
} }
return Translate(getsockopt_err); return Translate(getsockopt_err);
} }
@ -735,7 +735,7 @@ Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& o
} }
} }
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@ -748,17 +748,15 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) { if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger)); ASSERT(optval.size() == sizeof(Linger));
Linger linger; auto linger = GetValue<Linger>(optval);
std::memcpy(&linger, optval, sizeof(linger));
ASSERT(linger.onoff == 0 || linger.onoff == 1); ASSERT(linger.onoff == 0 || linger.onoff == 1);
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
} }
ASSERT(optlen == sizeof(u32)); ASSERT(optval.size() == sizeof(u32));
u32 value; auto value = GetValue<u32>(optval);
std::memcpy(&value, optval, sizeof(value));
switch (optname) { switch (optname) {
case OptName::REUSEADDR: case OptName::REUSEADDR:
@ -862,7 +860,7 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
} else { } else {
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() == sizeof(SockAddrIn));
const SockAddrIn result = Translate(addr_in); const SockAddrIn result = Translate(addr_in);
std::memcpy(addr.data(), &result, sizeof(result)); PutValue(addr, result);
} }
} }
@ -886,8 +884,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> mes
Network::SockAddrIn* p_addr_in = nullptr; Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) { if (!addr.empty()) {
ASSERT(addr.size() == sizeof(SockAddrIn)); ASSERT(addr.size() == sizeof(SockAddrIn));
SockAddrIn guest_addr_in; auto guest_addr_in = GetValue<SockAddrIn>(addr);
std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
addr_in = Translate(guest_addr_in); addr_in = Translate(guest_addr_in);
p_addr_in = &addr_in; p_addr_in = &addr_in;
} }

View File

@ -163,7 +163,7 @@ private:
Errno ListenImpl(s32 fd, s32 backlog); Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how); Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,

View File

@ -3,6 +3,7 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/control/channel_state.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/renderer_null/null_rasterizer.h" #include "video_core/renderer_null/null_rasterizer.h"
@ -99,8 +100,14 @@ bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
} }
void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {} const VideoCore::DiskResourceLoadCallback& callback) {}
void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {
void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} CreateChannel(channel);
void RasterizerNull::ReleaseChannel(s32 channel_id) {} }
void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {
BindToChannel(channel.bind_id);
}
void RasterizerNull::ReleaseChannel(s32 channel_id) {
EraseChannel(channel_id);
}
} // namespace Null } // namespace Null

View File

@ -137,6 +137,56 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
BlitScreen::~BlitScreen() = default; BlitScreen::~BlitScreen() = default;
static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer,
const ScreenInfo& screen_info) {
f32 left, top, right, bottom;
if (!framebuffer.crop_rect.IsEmpty()) {
// If crop rectangle is not empty, apply properties from rectangle.
left = static_cast<f32>(framebuffer.crop_rect.left);
top = static_cast<f32>(framebuffer.crop_rect.top);
right = static_cast<f32>(framebuffer.crop_rect.right);
bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
} else {
// Otherwise, fall back to framebuffer dimensions.
left = 0;
top = 0;
right = static_cast<f32>(framebuffer.width);
bottom = static_cast<f32>(framebuffer.height);
}
// Apply transformation flags.
auto framebuffer_transform_flags = framebuffer.transform_flags;
if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) {
// Switch left and right.
std::swap(left, right);
}
if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
// Switch top and bottom.
std::swap(top, bottom);
}
framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH;
framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV;
if (True(framebuffer_transform_flags)) {
UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
static_cast<u32>(framebuffer_transform_flags));
}
// Get the screen properties.
const f32 screen_width = static_cast<f32>(screen_info.width);
const f32 screen_height = static_cast<f32>(screen_info.height);
// Normalize coordinate space.
left /= screen_width;
top /= screen_height;
right /= screen_width;
bottom /= screen_height;
return Common::Rectangle<f32>(left, top, right, bottom);
}
void BlitScreen::Recreate() { void BlitScreen::Recreate() {
present_manager.WaitPresent(); present_manager.WaitPresent();
scheduler.Finish(); scheduler.Finish();
@ -354,17 +404,10 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
} }
if (fsr) { if (fsr) {
auto crop_rect = framebuffer.crop_rect; const auto crop_rect = NormalizeCrop(framebuffer, screen_info);
if (crop_rect.GetWidth() == 0) { const VkExtent2D fsr_input_size{
crop_rect.right = framebuffer.width; .width = Settings::values.resolution_info.ScaleUp(screen_info.width),
} .height = Settings::values.resolution_info.ScaleUp(screen_info.height),
if (crop_rect.GetHeight() == 0) {
crop_rect.bottom = framebuffer.height;
}
crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
VkExtent2D fsr_input_size{
.width = Settings::values.resolution_info.ScaleUp(framebuffer.width),
.height = Settings::values.resolution_info.ScaleUp(framebuffer.height),
}; };
VkImageView fsr_image_view = VkImageView fsr_image_view =
fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
@ -1397,61 +1440,37 @@ void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayou
void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
const Layout::FramebufferLayout layout) const { const Layout::FramebufferLayout layout) const {
const auto& framebuffer_transform_flags = framebuffer.transform_flags; f32 left, top, right, bottom;
const auto& framebuffer_crop_rect = framebuffer.crop_rect;
static constexpr Common::Rectangle<f32> texcoords{0.f, 0.f, 1.f, 1.f}; if (fsr) {
auto left = texcoords.left; // FSR has already applied the crop, so we just want to render the image
auto right = texcoords.right; // it has produced.
left = 0;
top = 0;
right = 1;
bottom = 1;
} else {
// Get the normalized crop rectangle.
const auto crop = NormalizeCrop(framebuffer, screen_info);
switch (framebuffer_transform_flags) { // Apply the crop.
case Service::android::BufferTransformFlags::Unset: left = crop.left;
break; top = crop.top;
case Service::android::BufferTransformFlags::FlipV: right = crop.right;
// Flip the framebuffer vertically bottom = crop.bottom;
left = texcoords.right;
right = texcoords.left;
break;
default:
UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
static_cast<u32>(framebuffer_transform_flags));
break;
}
UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
f32 left_start{};
if (framebuffer_crop_rect.Top() > 0) {
left_start = static_cast<f32>(framebuffer_crop_rect.Top()) /
static_cast<f32>(framebuffer_crop_rect.Bottom());
}
f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width);
f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height);
// Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
// (e.g. handheld mode) on a 1920x1080 framebuffer.
if (!fsr) {
if (framebuffer_crop_rect.GetWidth() > 0) {
scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
static_cast<f32>(screen_info.width);
}
if (framebuffer_crop_rect.GetHeight() > 0) {
scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
static_cast<f32>(screen_info.height);
}
} }
// Map the coordinates to the screen.
const auto& screen = layout.screen; const auto& screen = layout.screen;
const auto x = static_cast<f32>(screen.left); const auto x = static_cast<f32>(screen.left);
const auto y = static_cast<f32>(screen.top); const auto y = static_cast<f32>(screen.top);
const auto w = static_cast<f32>(screen.GetWidth()); const auto w = static_cast<f32>(screen.GetWidth());
const auto h = static_cast<f32>(screen.GetHeight()); const auto h = static_cast<f32>(screen.GetHeight());
data.vertices[0] = ScreenRectVertex(x, y, texcoords.top * scale_u, left_start + left * scale_v);
data.vertices[1] = data.vertices[0] = ScreenRectVertex(x, y, left, top);
ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v); data.vertices[1] = ScreenRectVertex(x + w, y, right, top);
data.vertices[2] = data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v); data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
data.vertices[3] =
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, left_start + right * scale_v);
} }
void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {

View File

@ -34,7 +34,7 @@ FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image
} }
VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect) { VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) {
UpdateDescriptorSet(image_index, image_view); UpdateDescriptorSet(image_index, image_view);
@ -61,15 +61,21 @@ VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView imag
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline);
std::array<u32, 4 * 4> push_constants; const f32 input_image_width = static_cast<f32>(input_image_extent.width);
FsrEasuConOffset( const f32 input_image_height = static_cast<f32>(input_image_extent.height);
push_constants.data() + 0, push_constants.data() + 4, push_constants.data() + 8, const f32 output_image_width = static_cast<f32>(output_size.width);
push_constants.data() + 12, const f32 output_image_height = static_cast<f32>(output_size.height);
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 viewport_x = crop_rect.left * input_image_width;
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
const f32 viewport_y = crop_rect.top * input_image_height;
static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), std::array<u32, 4 * 4> push_constants;
static_cast<f32>(input_image_extent.width), static_cast<f32>(input_image_extent.height), FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4,
static_cast<f32>(output_size.width), static_cast<f32>(output_size.height), push_constants.data() + 8, push_constants.data() + 12,
static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
viewport_width, viewport_height, input_image_width, input_image_height,
output_image_width, output_image_height, viewport_x, viewport_y);
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
{ {

View File

@ -17,7 +17,7 @@ public:
explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
VkExtent2D output_size); VkExtent2D output_size);
VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect); VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect);
private: private:
void CreateDescriptorPool(); void CreateDescriptorPool();

View File

@ -82,7 +82,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
} }
if (y_negate) { if (y_negate) {
y += height; y += conv(static_cast<f32>(regs.surface_clip.height));
height = -height; height = -height;
} }

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -1,4 +1,4 @@
// Text : Copyright 2022 yuzu Emulator Project // Text : Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once

View File

@ -152,7 +152,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
[this](bool is_handheld) { UpdateDockedState(is_handheld); }); [this](bool is_handheld) { UpdateDockedState(is_handheld); });
advanced = new ConfigureInputAdvanced(this); advanced = new ConfigureInputAdvanced(hid_core, this);
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced); ui->tabAdvanced->layout()->addWidget(advanced);

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -4,11 +4,13 @@
#include <QColorDialog> #include <QColorDialog>
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "ui_configure_input_advanced.h" #include "ui_configure_input_advanced.h"
#include "yuzu/configuration/configure_input_advanced.h" #include "yuzu/configuration/configure_input_advanced.h"
ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} {
ui->setupUi(this); ui->setupUi(this);
controllers_color_buttons = {{ controllers_color_buttons = {{
@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
player.button_color_left = colors[1]; player.button_color_left = colors[1];
player.body_color_right = colors[2]; player.body_color_right = colors[2];
player.button_color_right = colors[3]; player.button_color_right = colors[3];
hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings();
} }
Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();

View File

@ -14,11 +14,15 @@ namespace Ui {
class ConfigureInputAdvanced; class ConfigureInputAdvanced;
} }
namespace Core::HID {
class HIDCore;
} // namespace Core::HID
class ConfigureInputAdvanced : public QWidget { class ConfigureInputAdvanced : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureInputAdvanced(QWidget* parent = nullptr); explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr);
~ConfigureInputAdvanced() override; ~ConfigureInputAdvanced() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -44,4 +48,6 @@ private:
std::array<std::array<QColor, 4>, 8> controllers_colors; std::array<std::array<QColor, 4>, 8> controllers_colors;
std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons; std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
Core::HID::HIDCore& hid_core;
}; };

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() {
return; return;
} }
// Some games crash when the profile image is too big. Resize any image bigger than 256x256 // Profile image must be 256x256
QImage image(image_path); QImage image(image_path);
if (image.width() > 256 || image.height() > 256) { if (image.width() != 256 || image.height() != 256) {
image = image.scaled(256, 256, Qt::KeepAspectRatio); image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
if (!image.save(image_path)) { if (!image.save(image_path)) {
QMessageBox::warning(this, tr("Error resizing user image"), QMessageBox::warning(this, tr("Error resizing user image"),
tr("Unable to resize image")); tr("Unable to resize image"));

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once

View File

@ -156,7 +156,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
// Ui General // Ui General
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");

View File

@ -1908,7 +1908,10 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
StartGameType type, AmLaunchType launch_type) { StartGameType type, AmLaunchType launch_type) {
LOG_INFO(Frontend, "yuzu starting..."); LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
if (program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) {
StoreRecentFile(filename); // Put the filename on top of the list
}
// Save configurations // Save configurations
UpdateUISettings(); UpdateUISettings();
@ -2174,6 +2177,7 @@ void GMainWindow::ShutdownGame() {
return; return;
} }
play_time_manager->Stop();
OnShutdownBegin(); OnShutdownBegin();
OnEmulationStopTimeExpired(); OnEmulationStopTimeExpired();
OnEmulationStopped(); OnEmulationStopped();
@ -3484,7 +3488,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
} }
void GMainWindow::OnExit() { void GMainWindow::OnExit() {
OnStopGame(); ShutdownGame();
} }
void GMainWindow::OnSaveConfig() { void GMainWindow::OnSaveConfig() {
@ -4272,7 +4276,7 @@ void GMainWindow::OnToggleStatusBar() {
} }
void GMainWindow::OnAlbum() { void GMainWindow::OnAlbum() {
constexpr u64 AlbumId = 0x010000000000100Dull; constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"), QMessageBox::warning(this, tr("No firmware available"),
@ -4295,7 +4299,7 @@ void GMainWindow::OnAlbum() {
} }
void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
constexpr u64 CabinetId = 0x0100000000001002ull; constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"), QMessageBox::warning(this, tr("No firmware available"),
@ -4319,7 +4323,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
} }
void GMainWindow::OnMiiEdit() { void GMainWindow::OnMiiEdit() {
constexpr u64 MiiEditId = 0x0100000000001009ull; constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"), QMessageBox::warning(this, tr("No firmware available"),
@ -4847,7 +4851,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe
} }
bool GMainWindow::ConfirmClose() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { if (emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
return true;
}
if (!system->GetExitLocked() &&
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) {
return true; return true;
} }
const auto text = tr("Are you sure you want to close yuzu?"); const auto text = tr("Are you sure you want to close yuzu?");
@ -4952,7 +4961,7 @@ bool GMainWindow::ConfirmChangeGame() {
} }
bool GMainWindow::ConfirmForceLockedExit() { bool GMainWindow::ConfirmForceLockedExit() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { if (emu_thread == nullptr) {
return true; return true;
} }
const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"

View File

@ -93,10 +93,6 @@ struct Values {
Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui}; Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
Setting<bool> confirm_before_closing{
linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
true, true};
SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
ConfirmStop::Ask_Always, ConfirmStop::Ask_Always,
"confirmStop", "confirmStop",

View File

@ -31,6 +31,7 @@ void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
// Create a test window with a Vulkan surface type for checking present modes. // Create a test window with a Vulkan surface type for checking present modes.
QWindow test_window(window); QWindow test_window(window);
test_window.setSurfaceType(QWindow::VulkanSurface); test_window.setSurfaceType(QWindow::VulkanSurface);
test_window.create();
auto wsi = QtCommon::GetWindowSystemInfo(&test_window); auto wsi = QtCommon::GetWindowSystemInfo(&test_window);
vk::InstanceDispatch dld; vk::InstanceDispatch dld;