Compare commits

..

2 Commits

Author SHA1 Message Date
98ec6ba3ee Android #121 2023-11-03 00:56:55 +00:00
49ffcc04a6 Merge PR 11943 2023-11-03 00:56:55 +00:00
77 changed files with 1609 additions and 1714 deletions

View File

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

View File

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

View File

@ -1,5 +1,6 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [11943](https://github.com/yuzu-emu/yuzu//pull/11943) | [`41701052d`](https://github.com/yuzu-emu/yuzu//pull/11943/files) | renderer_vulkan: minimize transform feedback support log | [liamwhite](https://github.com/liamwhite/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -21,7 +21,7 @@ if (MSVC)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN) add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects are built with Unicode support. # Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE) add_definitions(-DUNICODE -D_UNICODE)
# /W4 - Level 4 warnings # /W4 - Level 4 warnings
@ -54,11 +54,11 @@ if (MSVC)
/GT /GT
# Modules # Modules
/experimental:module- # Explicitly disable module support due to conflicts with precompiled headers. /experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
# External headers diagnostics # External headers diagnostics
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers /external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them. /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
# Warnings # Warnings
/W4 /W4

View File

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

View File

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

View File

@ -107,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) {
Toast.makeText( Toast.makeText(
this, this,
getString( getString(

View File

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

View File

@ -403,7 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else { } else {
firmwarePath.deleteRecursively() firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true) cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem()
getString(R.string.save_file_imported_success) getString(R.string.save_file_imported_success)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -649,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
// Reinitialize relevant data // Reinitialize relevant data
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem()
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(false) NativeLibrary.initializeSystem()
areDirectoriesReady = true areDirectoriesReady = true
} }
} }

View File

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

View File

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

View File

@ -199,8 +199,8 @@ bool EmulationSession::IsPaused() const {
return m_is_running && m_is_paused; return m_is_running && m_is_paused;
} }
const Core::PerfStatsResults& EmulationSession::PerfStats() { const Core::PerfStatsResults& EmulationSession::PerfStats() const {
m_perf_stats = m_system.GetAndResetPerfStats(); std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats; return m_perf_stats;
} }
@ -247,13 +247,11 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
} }
} }
void EmulationSession::InitializeSystem(bool reload) { void EmulationSession::InitializeSystem() {
if (!reload) { // Initialize logging system
// Initialize logging system Common::Log::Initialize();
Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::SetColorConsoleBackendEnabled(true); Common::Log::Start();
Common::Log::Start();
}
// Initialize filesystem. // Initialize filesystem.
m_system.SetFilesystem(m_vfs); m_system.SetFilesystem(m_vfs);
@ -383,6 +381,11 @@ void EmulationSession::RunEmulation() {
break; break;
} }
} }
{
// Refresh performance stats.
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
m_perf_stats = m_system.GetAndResetPerfStats();
}
} }
} }
@ -664,15 +667,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
} }
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) {
jboolean reload) {
// Create the default config.ini. // Create the default config.ini.
Config{}; Config{};
// Initialize the emulated system. // Initialize the emulated system.
if (!reload) { EmulationSession::GetInstance().System().Initialize();
EmulationSession::GetInstance().System().Initialize(); EmulationSession::GetInstance().InitializeSystem();
}
EmulationSession::GetInstance().InitializeSystem(reload);
} }
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) { jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {

View File

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

View File

@ -134,18 +134,16 @@
<FrameLayout <FrameLayout
android:id="@+id/overlay_container" android:id="@+id/overlay_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:fitsSystemWindows="true">
<com.google.android.material.textview.MaterialTextView <TextView
android:id="@+id/show_fps_text" android:id="@+id/show_fps_text"
style="@style/TextAppearance.Material3.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:paddingHorizontal="20dp" android:shadowColor="@android:color/black"
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 stereo_stream_count) { bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) {
return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 && return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
stereo_stream_count >= 0 && stereo_stream_count <= total_stream_count; sterero_stream_count > 0 && sterero_stream_count <= total_stream_count;
} }
} // namespace } // namespace

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,14 +35,13 @@ struct RomFSHeader {
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct DirectoryEntry { struct DirectoryEntry {
u32_le parent;
u32_le sibling; u32_le sibling;
u32_le child_dir; u32_le child_dir;
u32_le child_file; u32_le child_file;
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
struct FileEntry { struct FileEntry {
u32_le parent; u32_le parent;
@ -65,22 +64,25 @@ std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offs
return {entry, string}; return {entry, string};
} }
void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) { u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
while (this_file_offset != ROMFS_ENTRY_EMPTY) { while (true) {
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
parent->AddFile(std::make_shared<OffsetVfsFile>( parent->AddFile(std::make_shared<OffsetVfsFile>(
file, entry.first.size, entry.first.offset + data_offset, entry.second)); file, entry.first.size, entry.first.offset + data_offset, entry.second));
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_file_offset = entry.first.sibling; this_file_offset = entry.first.sibling;
} }
} }
void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
std::size_t data_offset, u32 this_dir_offset, std::size_t data_offset, u32 this_dir_offset,
std::shared_ptr<VectorVfsDirectory>& parent) { std::shared_ptr<VectorVfsDirectory> parent) {
while (this_dir_offset != ROMFS_ENTRY_EMPTY) { while (true) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>( auto current = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
@ -95,12 +97,14 @@ void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size
} }
parent->AddDirectory(current); parent->AddDirectory(current);
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
this_dir_offset = entry.first.sibling; this_dir_offset = entry.first.sibling;
} }
} }
} // Anonymous namespace } // Anonymous namespace
VirtualDir ExtractRomFS(VirtualFile file) { VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
RomFSHeader header{}; RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader)) if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr; return nullptr;
@ -109,17 +113,27 @@ VirtualDir ExtractRomFS(VirtualFile file) {
return nullptr; return nullptr;
const u64 file_offset = header.file_meta.offset; const u64 file_offset = header.file_meta.offset;
const u64 dir_offset = header.directory_meta.offset; const u64 dir_offset = header.directory_meta.offset + 4;
auto root_container = std::make_shared<VectorVfsDirectory>(); auto root =
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
file->GetName(), file->GetContainingDirectory());
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
if (auto root = root_container->GetSubdirectory(""); root) { VirtualDir out = std::move(root);
return std::make_shared<CachedVfsDirectory>(std::move(root));
if (type == RomFSExtractionType::SingleDiscard)
return out->GetSubdirectories().front();
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();
} }
return nullptr; return std::make_shared<CachedVfsDirectory>(std::move(out));
} }
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {

View File

@ -7,9 +7,16 @@
namespace FileSys { namespace FileSys {
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
SingleDiscard, // Traverses into the first subdirectory of root
};
// Converts a RomFS binary blob to VFS Filesystem // Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure // Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file); VirtualDir ExtractRomFS(VirtualFile file,
RomFSExtractionType type = RomFSExtractionType::Truncated);
// Converts a VFS filesystem into a RomFS binary // Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure // Returns nullptr on failure

View File

@ -96,7 +96,18 @@ void EmulatedController::ReloadFromSettings() {
} }
controller.color_values = {}; controller.color_values = {};
ReloadColorsFromSettings(); 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),
};
ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
@ -117,30 +128,6 @@ 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];
@ -1104,30 +1091,30 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
bool is_charging = false; bool is_charging = false;
bool is_powered = false; bool is_powered = false;
NpadBatteryLevel battery_level = NpadBatteryLevel::Empty; NpadBatteryLevel battery_level = 0;
switch (controller.battery_values[index]) { switch (controller.battery_values[index]) {
case Common::Input::BatteryLevel::Charging: case Common::Input::BatteryLevel::Charging:
is_charging = true; is_charging = true;
is_powered = true; is_powered = true;
battery_level = NpadBatteryLevel::Full; battery_level = 6;
break; break;
case Common::Input::BatteryLevel::Medium: case Common::Input::BatteryLevel::Medium:
battery_level = NpadBatteryLevel::High; battery_level = 6;
break; break;
case Common::Input::BatteryLevel::Low: case Common::Input::BatteryLevel::Low:
battery_level = NpadBatteryLevel::Low; battery_level = 4;
break; break;
case Common::Input::BatteryLevel::Critical: case Common::Input::BatteryLevel::Critical:
battery_level = NpadBatteryLevel::Critical; battery_level = 2;
break; break;
case Common::Input::BatteryLevel::Empty: case Common::Input::BatteryLevel::Empty:
battery_level = NpadBatteryLevel::Empty; battery_level = 0;
break; break;
case Common::Input::BatteryLevel::None: case Common::Input::BatteryLevel::None:
case Common::Input::BatteryLevel::Full: case Common::Input::BatteryLevel::Full:
default: default:
is_powered = true; is_powered = true;
battery_level = NpadBatteryLevel::Full; battery_level = 8;
break; break;
} }

View File

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

@ -302,15 +302,6 @@ enum class TouchScreenModeForNx : u8 {
Heat2, Heat2,
}; };
// This is nn::hid::system::NpadBatteryLevel
enum class NpadBatteryLevel : u32 {
Empty,
Critical,
Low,
High,
Full,
};
// This is nn::hid::NpadStyleTag // This is nn::hid::NpadStyleTag
struct NpadStyleTag { struct NpadStyleTag {
union { union {
@ -394,12 +385,16 @@ struct NpadGcTriggerState {
}; };
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size"); static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::system::NpadBatteryLevel
using NpadBatteryLevel = u32;
static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
// This is nn::hid::system::NpadPowerInfo // This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo { struct NpadPowerInfo {
bool is_powered{}; bool is_powered{};
bool is_charging{}; bool is_charging{};
INSERT_PADDING_BYTES(0x6); INSERT_PADDING_BYTES(0x6);
NpadBatteryLevel battery_level{NpadBatteryLevel::Full}; NpadBatteryLevel battery_level{8};
}; };
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");

View File

@ -3,13 +3,11 @@
#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"
@ -40,36 +38,9 @@ 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 void JPGToMemory(void* context, void* data, int len) { static constexpr u32 SanitizeJPEGSize(std::size_t size) {
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;
constexpr int profile_dimensions = 256; return static_cast<u32>(std::min(size, max_jpeg_image_size));
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> {
@ -368,20 +339,19 @@ 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(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
return; return;
} }
std::vector<u8> buffer(image.GetSize()); const u32 size = SanitizeJPEGSize(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(static_cast<u32>(buffer.size())); rb.Push<u32>(size);
} }
void GetImageSize(HLERequestContext& ctx) { void GetImageSize(HLERequestContext& ctx) {
@ -395,18 +365,10 @@ 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(static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
return; } else {
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

@ -330,7 +330,8 @@ void WebBrowser::ExtractOfflineRomFS() {
LOG_DEBUG(Service_AM, "Extracting RomFS to {}", LOG_DEBUG(Service_AM, "Extracting RomFS to {}",
Common::FS::PathToUTF8String(offline_cache_dir)); Common::FS::PathToUTF8String(offline_cache_dir));
const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto extracted_romfs_dir =
FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
const auto temp_dir = system.GetFilesystem()->CreateDirectory( const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);

View File

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

@ -1108,9 +1108,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
shared_memory->sixaxis_dual_right_properties.raw = 0; shared_memory->sixaxis_dual_right_properties.raw = 0;
shared_memory->sixaxis_left_properties.raw = 0; shared_memory->sixaxis_left_properties.raw = 0;
shared_memory->sixaxis_right_properties.raw = 0; shared_memory->sixaxis_right_properties.raw = 0;
shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty; shared_memory->battery_level_dual = 0;
shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty; shared_memory->battery_level_left = 0;
shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty; shared_memory->battery_level_right = 0;
shared_memory->fullkey_color = { shared_memory->fullkey_color = {
.attribute = ColorAttribute::NoController, .attribute = ColorAttribute::NoController,
.fullkey = {}, .fullkey = {},

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 is_enabled; bool unintended_home_button_input_protection;
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,11 +1364,13 @@ 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.is_enabled, parameters.npad_id); parameters.unintended_home_button_input_protection, parameters.npad_id);
LOG_DEBUG(Service_HID, LOG_WARNING(Service_HID,
"(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); "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

@ -32,15 +32,15 @@ struct Lifo {
} }
std::size_t GetPreviousEntryIndex() const { std::size_t GetPreviousEntryIndex() const {
return static_cast<size_t>((buffer_tail + max_buffer_size - 1) % max_buffer_size); return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
} }
std::size_t GetNextEntryIndex() const { std::size_t GetNextEntryIndex() const {
return static_cast<size_t>((buffer_tail + 1) % max_buffer_size); return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
} }
void WriteNextEntry(const State& new_state) { void WriteNextEntry(const State& new_state) {
if (buffer_count < static_cast<s64>(max_buffer_size) - 1) { if (buffer_count < total_buffer_count - 1) {
buffer_count++; buffer_count++;
} }
buffer_tail = GetNextEntryIndex(); buffer_tail = GetNextEntryIndex();

View File

@ -1,159 +0,0 @@
// 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,7 +11,6 @@
#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"
@ -34,21 +33,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 WrapFixed(this, &nvhost_as_gpu::BindChannel, input, output); return BindChannel(input, output);
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_as_gpu::AllocateSpace, input, output); return AllocateSpace(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_as_gpu::FreeSpace, input, output); return FreeSpace(input, output);
case 0x5: case 0x5:
return WrapFixed(this, &nvhost_as_gpu::UnmapBuffer, input, output); return UnmapBuffer(input, output);
case 0x6: case 0x6:
return WrapFixed(this, &nvhost_as_gpu::MapBufferEx, input, output); return MapBufferEx(input, output);
case 0x8: case 0x8:
return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output); return GetVARegions(input, output);
case 0x9: case 0x9:
return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output); return AllocAsEx(input, output);
case 0x14: case 0x14:
return WrapVariable(this, &nvhost_as_gpu::Remap, input, output); return Remap(input, output);
default: default:
break; break;
} }
@ -73,8 +72,7 @@ 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 WrapFixedInlOut(this, &nvhost_as_gpu::GetVARegions3, input, output, return GetVARegions(input, output, inline_output);
inline_output);
default: default:
break; break;
} }
@ -89,7 +87,10 @@ 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(IoctlAllocAsEx& params) { NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) {
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);
@ -140,7 +141,10 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) {
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);
@ -190,6 +194,7 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
.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;
} }
@ -217,7 +222,10 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
mapping_map.erase(offset); mapping_map.erase(offset);
} }
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) {
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);
@ -256,11 +264,18 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
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<IoctlRemapEntry> entries) { NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", entries.size()); const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
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;
@ -302,10 +317,14 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
} }
} }
std::memcpy(output.data(), entries.data(), output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) {
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={}",
@ -402,10 +421,14 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
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(IoctlUnmapBuffer& params) { NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {
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);
@ -441,7 +464,9 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) { NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) {
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);
@ -468,7 +493,10 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
}; };
} }
NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) { NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> 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);
@ -480,10 +508,15 @@ NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) {
GetVARegionsImpl(params); GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions) { NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output,
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);
@ -495,10 +528,9 @@ NvResult nvhost_as_gpu::GetVARegions3(IoctlGetVaRegions& params, std::span<VaReg
GetVARegionsImpl(params); GetVARegionsImpl(params);
const size_t num_regions = std::min(params.regions.size(), regions.size()); std::memcpy(output.data(), &params, output.size());
for (size_t i = 0; i < num_regions; i++) { std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion));
regions[i] = params.regions[i]; std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion));
}
return NvResult::Success; return NvResult::Success;
} }

View File

@ -139,17 +139,18 @@ 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(IoctlAllocAsEx& params); NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output);
NvResult AllocateSpace(IoctlAllocSpace& params); NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output);
NvResult Remap(std::span<IoctlRemapEntry> params); NvResult Remap(std::span<const u8> input, std::span<u8> output);
NvResult MapBufferEx(IoctlMapBufferEx& params); NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output);
NvResult UnmapBuffer(IoctlUnmapBuffer& params); NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
NvResult FreeSpace(IoctlFreeSpace& params); NvResult FreeSpace(std::span<const u8> input, std::span<u8> output);
NvResult BindChannel(IoctlBindChannel& params); NvResult BindChannel(std::span<const u8> input, std::span<u8> output);
void GetVARegionsImpl(IoctlGetVaRegions& params); void GetVARegionsImpl(IoctlGetVaRegions& params);
NvResult GetVARegions1(IoctlGetVaRegions& params); NvResult GetVARegions(std::span<const u8> input, std::span<u8> output);
NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions); NvResult GetVARegions(std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output);
void FreeMappingLocked(u64 offset); void FreeMappingLocked(u64 offset);
@ -212,6 +213,7 @@ 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,7 +14,6 @@
#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"
@ -41,19 +40,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 WrapFixed(this, &nvhost_ctrl::NvOsGetConfigU32, input, output); return NvOsGetConfigU32(input, output);
case 0x1c: case 0x1c:
return WrapFixed(this, &nvhost_ctrl::IocCtrlClearEventWait, input, output); return IocCtrlClearEventWait(input, output);
case 0x1d: case 0x1d:
return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, true); return IocCtrlEventWait(input, output, true);
case 0x1e: case 0x1e:
return WrapFixed(this, &nvhost_ctrl::IocCtrlEventWait, input, output, false); return IocCtrlEventWait(input, output, false);
case 0x1f: case 0x1f:
return WrapFixed(this, &nvhost_ctrl::IocCtrlEventRegister, input, output); return IocCtrlEventRegister(input, output);
case 0x20: case 0x20:
return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregister, input, output); return IocCtrlEventUnregister(input, output);
case 0x21: case 0x21:
return WrapFixed(this, &nvhost_ctrl::IocCtrlEventUnregisterBatch, input, output); return IocCtrlEventUnregisterBatch(input, output);
} }
break; break;
default: default:
@ -80,19 +79,25 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl::NvOsGetConfigU32(IocGetConfigParams& params) { NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) {
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(IocCtrlEventWaitParams& params, bool is_allocation) { NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output,
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;
} }
@ -226,7 +231,9 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) { NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) {
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) {
@ -245,7 +252,9 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(IocCtrlEventRegisterParams& params) {
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params) { NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) {
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);
@ -253,7 +262,9 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(IocCtrlEventUnregisterParams& param
return FreeEvent(event_id); return FreeEvent(event_id);
} }
NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params) { NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) {
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);
@ -269,7 +280,10 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchPar
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl::IocCtrlClearEventWait(IocCtrlEventClearParams& params) { NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) {
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(IocGetConfigParams& params); NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output);
NvResult IocCtrlEventRegister(IocCtrlEventRegisterParams& params); NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation);
NvResult IocCtrlEventUnregister(IocCtrlEventUnregisterParams& params); NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output);
NvResult IocCtrlEventUnregisterBatch(IocCtrlEventUnregisterBatchParams& params); NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output);
NvResult IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_allocation); NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output);
NvResult IocCtrlClearEventWait(IocCtrlEventClearParams& params); NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output);
NvResult FreeEvent(u32 slot); NvResult FreeEvent(u32 slot);

View File

@ -6,7 +6,6 @@
#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"
@ -28,23 +27,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 WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetCtxSize, input, output); return ZCullGetCtxSize(input, output);
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_ctrl_gpu::ZCullGetInfo, input, output); return ZCullGetInfo(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_ctrl_gpu::ZBCSetTable, input, output); return ZBCSetTable(input, output);
case 0x4: case 0x4:
return WrapFixed(this, &nvhost_ctrl_gpu::ZBCQueryTable, input, output); return ZBCQueryTable(input, output);
case 0x5: case 0x5:
return WrapFixed(this, &nvhost_ctrl_gpu::GetCharacteristics1, input, output); return GetCharacteristics(input, output);
case 0x6: case 0x6:
return WrapFixed(this, &nvhost_ctrl_gpu::GetTPCMasks1, input, output); return GetTPCMasks(input, output);
case 0x7: case 0x7:
return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output); return FlushL2(input, output);
case 0x14: case 0x14:
return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output); return GetActiveSlotMask(input, output);
case 0x1c: case 0x1c:
return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output); return GetGpuTime(input, output);
default: default:
break; break;
} }
@ -66,11 +65,9 @@ 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 WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output, return GetCharacteristics(input, output, inline_output);
inline_output);
case 0x6: case 0x6:
return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetTPCMasks3, input, output, return GetTPCMasks(input, output, inline_output);
inline_output);
default: default:
break; break;
} }
@ -85,8 +82,10 @@ 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::GetCharacteristics1(IoctlCharacteristics& params) { NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) {
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;
@ -124,13 +123,15 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
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::GetCharacteristics3( NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output,
IoctlCharacteristics& params, std::span<IoctlGpuCharacteristics> gpu_characteristics) { std::span<u8> inline_output) {
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;
@ -168,47 +169,70 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics3(
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()) {
gpu_characteristics.front() = params.gc; std::memcpy(output.data(), &params, output.size());
} std::memcpy(inline_output.data(), &params.gc, inline_output.size());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) { NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> 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());
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) { NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output,
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;
} }
if (!tpc_mask.empty()) { std::memcpy(output.data(), &params, output.size());
tpc_mask.front() = params.tcp_mask; std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
}
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) { NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) {
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(IoctlZcullGetCtxSize& params) { NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) {
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(IoctlNvgpuGpuZcullGetInfoArgs& params) { NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) {
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;
@ -219,28 +243,53 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) {
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(IoctlZbcSetTable& params) { NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) {
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(IoctlZbcQueryTable& params) { NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) {
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(IoctlFlushL2& params) { NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) {
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(IoctlGetGpuTime& params) { NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) {
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,20 +151,21 @@ private:
}; };
static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
NvResult GetCharacteristics1(IoctlCharacteristics& params); NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output);
NvResult GetCharacteristics3(IoctlCharacteristics& params, NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output,
std::span<IoctlGpuCharacteristics> gpu_characteristics); std::span<u8> inline_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); NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output);
NvResult GetActiveSlotMask(IoctlActiveSlotMask& params); NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output);
NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params); NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output);
NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params); NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output);
NvResult ZBCSetTable(IoctlZbcSetTable& params); NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output);
NvResult ZBCQueryTable(IoctlZbcQueryTable& params); NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output);
NvResult FlushL2(IoctlFlushL2& params); NvResult FlushL2(std::span<const u8> input, std::span<u8> output);
NvResult GetGpuTime(IoctlGetGpuTime& params); NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);
EventInterface& events_interface; EventInterface& events_interface;

View File

@ -8,7 +8,6 @@
#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"
@ -53,7 +52,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 WrapFixed(this, &nvhost_gpu::GetWaitbase, input, output); return GetWaitbase(input, output);
default: default:
break; break;
} }
@ -61,25 +60,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 WrapFixed(this, &nvhost_gpu::SetNVMAPfd, input, output); return SetNVMAPfd(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output); return ChannelSetTimeout(input, output);
case 0x8: case 0x8:
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false); return SubmitGPFIFOBase(input, output, false);
case 0x9: case 0x9:
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output); return AllocateObjectContext(input, output);
case 0xb: case 0xb:
return WrapFixed(this, &nvhost_gpu::ZCullBind, input, output); return ZCullBind(input, output);
case 0xc: case 0xc:
return WrapFixed(this, &nvhost_gpu::SetErrorNotifier, input, output); return SetErrorNotifier(input, output);
case 0xd: case 0xd:
return WrapFixed(this, &nvhost_gpu::SetChannelPriority, input, output); return SetChannelPriority(input, output);
case 0x1a: case 0x1a:
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output); return AllocGPFIFOEx2(input, output);
case 0x1b: case 0x1b:
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true); return SubmitGPFIFOBase(input, output, true);
case 0x1d: case 0x1d:
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output); return ChannelSetTimeslice(input, output);
default: default:
break; break;
} }
@ -87,9 +86,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 WrapFixed(this, &nvhost_gpu::SetClientData, input, output); return SetClientData(input, output);
case 0x15: case 0x15:
return WrapFixed(this, &nvhost_gpu::GetClientData, input, output); return GetClientData(input, output);
default: default:
break; break;
} }
@ -105,8 +104,7 @@ 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 WrapFixedInlIn(this, &nvhost_gpu::SubmitGPFIFOBase2, input, inline_input, return SubmitGPFIFOBase(input, inline_input, output);
output);
} }
break; break;
} }
@ -123,45 +121,63 @@ 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(IoctlSetNvmapFD& params) { NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
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(IoctlClientData& params) { NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) {
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(IoctlClientData& params) { NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) {
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(IoctlZCullBind& params) { NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) {
zcull_params = params; std::memcpy(&zcull_params, input.data(), input.size());
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(IoctlSetErrorNotifier& params) { NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) {
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(IoctlChannelSetPriority& params) { NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) {
channel_priority = params.priority; std::memcpy(&channel_priority, input.data(), input.size());
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(IoctlAllocGpfifoEx2& params) { NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) {
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}",
@ -177,14 +193,18 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params) {
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(IoctlAllocObjCtx& params) { NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) {
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;
} }
@ -228,7 +248,8 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementW
return result; return result;
} }
NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries) { NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
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);
@ -269,55 +290,65 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
flags.raw = 0; flags.raw = 0;
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
std::span<Tegra::CommandListHeader> commands, bool kickoff) { bool kickoff) {
if (params.num_entries > commands.size()) { if (input.size() < sizeof(IoctlSubmitGpfifo)) {
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(), commands.data(), std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader)); params.num_entries * sizeof(Tegra::CommandListHeader));
} }
return SubmitGPFIFOImpl(params, std::move(entries)); return SubmitGPFIFOImpl(params, output, std::move(entries));
} }
NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
std::span<const Tegra::CommandListHeader> commands) { std::span<u8> output) {
if (params.num_entries > commands.size()) { if (input.size() < sizeof(IoctlSubmitGpfifo)) {
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(), commands.data(), std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
params.num_entries * sizeof(Tegra::CommandListHeader)); return SubmitGPFIFOImpl(params, output, std::move(entries));
return SubmitGPFIFOImpl(params, std::move(entries));
} }
NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) { NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
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(IoctlChannelSetTimeout& params) { NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) {
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(IoctlSetTimeslice& params) { NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) {
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,24 +186,23 @@ private:
u32_le channel_priority{}; u32_le channel_priority{};
u32_le channel_timeslice{}; u32_le channel_timeslice{};
NvResult SetNVMAPfd(IoctlSetNvmapFD& params); NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
NvResult SetClientData(IoctlClientData& params); NvResult SetClientData(std::span<const u8> input, std::span<u8> output);
NvResult GetClientData(IoctlClientData& params); NvResult GetClientData(std::span<const u8> input, std::span<u8> output);
NvResult ZCullBind(IoctlZCullBind& params); NvResult ZCullBind(std::span<const u8> input, std::span<u8> output);
NvResult SetErrorNotifier(IoctlSetErrorNotifier& params); NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output);
NvResult SetChannelPriority(IoctlChannelSetPriority& params); NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output);
NvResult AllocGPFIFOEx2(IoctlAllocGpfifoEx2& params); NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output);
NvResult AllocateObjectContext(IoctlAllocObjCtx& params); NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output);
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries); Tegra::CommandList&& entries);
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,
std::span<Tegra::CommandListHeader> commands, bool kickoff = false); bool kickoff = false);
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
std::span<const Tegra::CommandListHeader> commands); std::span<u8> output);
NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
NvResult GetWaitbase(IoctlGetWaitbase& params); NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output);
NvResult ChannelSetTimeout(IoctlChannelSetTimeout& params); NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output);
NvResult ChannelSetTimeslice(IoctlSetTimeslice& params);
EventInterface& events_interface; EventInterface& events_interface;
NvCore::Container& core; NvCore::Container& core;

View File

@ -6,7 +6,6 @@
#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"
@ -26,18 +25,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 WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd); return Submit(fd, input, output);
} }
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output); return GetSyncpoint(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output); return GetWaitbase(input, output);
case 0x7: case 0x7:
return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); return SetSubmitTimeout(input, output);
case 0x9: case 0x9:
return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output); return MapBuffer(input, output);
case 0xa: case 0xa:
return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); return UnmapBuffer(input, output);
default: default:
break; break;
} }
@ -45,7 +44,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 WrapFixed(this, &nvhost_nvdec::SetNVMAPfd, input, output); return SetNVMAPfd(input);
default: default:
break; break;
} }

View File

@ -29,9 +29,6 @@ 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;
} }
@ -44,9 +41,6 @@ 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;
} }
@ -69,14 +63,18 @@ 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(IoctlSetNvmapFD& params) { NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
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(IoctlSubmit& params, std::span<u8> data, DeviceFD fd) { NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) {
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
@ -87,12 +85,12 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
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 = 0; std::size_t offset = sizeof(IoctlSubmit);
offset += SliceVectors(data, command_buffers, params.cmd_buffer_count, offset); offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
offset += SliceVectors(data, relocs, params.relocation_count, offset); offset += SliceVectors(input, relocs, params.relocation_count, offset);
offset += SliceVectors(data, reloc_shifts, params.relocation_count, offset); offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset);
offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset); offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); offset += SliceVectors(input, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU(); auto& gpu = system.GPU();
if (gpu.UseNvdec()) { if (gpu.UseNvdec()) {
@ -110,51 +108,72 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
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 = 0; offset = sizeof(IoctlSubmit);
offset += WriteVectors(data, command_buffers, offset); offset += WriteVectors(output, command_buffers, offset);
offset += WriteVectors(data, relocs, offset); offset += WriteVectors(output, relocs, offset);
offset += WriteVectors(data, reloc_shifts, offset); offset += WriteVectors(output, reloc_shifts, offset);
offset += WriteVectors(data, syncpt_increments, offset); offset += WriteVectors(output, syncpt_increments, offset);
offset += WriteVectors(data, fence_thresholds, offset); offset += WriteVectors(output, fence_thresholds, offset);
return NvResult::Success; return NvResult::Success;
} }
NvResult nvhost_nvdec_common::GetSyncpoint(IoctlGetSyncpoint& params) { NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) {
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(IoctlGetWaitbase& params) { NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) {
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(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) { NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) {
const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); IoctlMapBuffer params{};
for (size_t i = 0; i < num_entries; i++) { std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
entries[i].map_address = nvmap.PinHandle(entries[i].map_handle); 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) {
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::UnmapBuffer(IoctlMapBuffer& params, NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) {
std::span<MapBufferEntry> entries) { std::memcpy(&submit_timeout, input.data(), input.size());
const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size()));
for (size_t i = 0; i < num_entries; i++) {
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(IoctlSetNvmapFD&); NvResult SetNVMAPfd(std::span<const u8> input);
NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd); NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output);
NvResult GetSyncpoint(IoctlGetSyncpoint& params); NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output);
NvResult GetWaitbase(IoctlGetWaitbase& params); NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output);
NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult MapBuffer(std::span<const u8> input, std::span<u8> output);
NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output);
NvResult SetSubmitTimeout(u32 timeout); NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output);
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -5,7 +5,6 @@
#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 {
@ -19,7 +18,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 WrapFixed(this, &nvhost_nvjpg::SetNVMAPfd, input, output); return SetNVMAPfd(input, output);
default: default:
break; break;
} }
@ -47,7 +46,9 @@ 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(IoctlSetNvmapFD& params) { NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {
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(IoctlSetNvmapFD& params); NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);
}; };
} // namespace Service::Nvidia::Devices } // namespace Service::Nvidia::Devices

View File

@ -5,7 +5,6 @@
#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"
@ -26,16 +25,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 WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd); return Submit(fd, input, output);
} }
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output); return GetSyncpoint(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output); return GetWaitbase(input, output);
case 0x9: case 0x9:
return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output); return MapBuffer(input, output);
case 0xa: case 0xa:
return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output); return UnmapBuffer(input, output);
default: default:
break; break;
} }
@ -43,7 +42,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 WrapFixed(this, &nvhost_vic::SetNVMAPfd, input, output); return SetNVMAPfd(input);
default: default:
break; break;
} }

View File

@ -13,7 +13,6 @@
#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"
@ -32,17 +31,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 WrapFixed(this, &nvmap::IocCreate, input, output); return IocCreate(input, output);
case 0x3: case 0x3:
return WrapFixed(this, &nvmap::IocFromId, input, output); return IocFromId(input, output);
case 0x4: case 0x4:
return WrapFixed(this, &nvmap::IocAlloc, input, output); return IocAlloc(input, output);
case 0x5: case 0x5:
return WrapFixed(this, &nvmap::IocFree, input, output); return IocFree(input, output);
case 0x9: case 0x9:
return WrapFixed(this, &nvmap::IocParam, input, output); return IocParam(input, output);
case 0xe: case 0xe:
return WrapFixed(this, &nvmap::IocGetId, input, output); return IocGetId(input, output);
default: default:
break; break;
} }
@ -70,7 +69,9 @@ 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(IocCreateParams& params) { NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {
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{};
@ -84,10 +85,13 @@ NvResult nvmap::IocCreate(IocCreateParams& params) {
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(IocAllocParams& params) { NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {
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) {
@ -129,10 +133,14 @@ NvResult nvmap::IocAlloc(IocAllocParams& params) {
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(IocGetIdParams& params) { NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {
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
@ -149,10 +157,14 @@ NvResult nvmap::IocGetId(IocGetIdParams& params) {
} }
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(IocFromIdParams& params) { NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {
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
@ -176,12 +188,16 @@ NvResult nvmap::IocFromId(IocFromIdParams& params) {
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(IocParamParams& params) { NvResult nvmap::IocParam(std::span<const u8> input, std::span<u8> output) {
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) {
@ -221,10 +237,14 @@ NvResult nvmap::IocParam(IocParamParams& params) {
return NvResult::BadValue; return NvResult::BadValue;
} }
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success; return NvResult::Success;
} }
NvResult nvmap::IocFree(IocFreeParams& params) { NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {
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) {
@ -247,6 +267,7 @@ NvResult nvmap::IocFree(IocFreeParams& params) {
// 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(IocCreateParams& params); NvResult IocCreate(std::span<const u8> input, std::span<u8> output);
NvResult IocAlloc(IocAllocParams& params); NvResult IocAlloc(std::span<const u8> input, std::span<u8> output);
NvResult IocGetId(IocGetIdParams& params); NvResult IocGetId(std::span<const u8> input, std::span<u8> output);
NvResult IocFromId(IocFromIdParams& params); NvResult IocFromId(std::span<const u8> input, std::span<u8> output);
NvResult IocParam(IocParamParams& params); NvResult IocParam(std::span<const u8> input, std::span<u8> output);
NvResult IocFree(IocFreeParams& params); NvResult IocFree(std::span<const u8> input, std::span<u8> output);
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,7 +3,6 @@
#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 {
@ -22,6 +21,5 @@ 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,17 +71,24 @@ 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_params{ Nvidia::Devices::nvmap::IocCreateParams create_in_params{
.size = size, .size = size,
.handle = 0, .handle = 0,
}; };
R_UNLESS(nvmap.IocCreate(create_params) == Nvidia::NvResult::Success, Nvidia::Devices::nvmap::IocCreateParams create_out_params{};
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_params.handle; *out_nv_map_handle = create_out_params.handle;
// We succeeded. // We succeeded.
R_SUCCEED(); R_SUCCEED();
@ -89,10 +96,13 @@ 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_params{ Nvidia::Devices::nvmap::IocFreeParams free_in_params{
.handle = handle, .handle = handle,
}; };
R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); Nvidia::Devices::nvmap::IocFreeParams free_out_params{};
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();
@ -101,7 +111,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_params{ Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{
.handle = handle, .handle = handle,
.heap_mask = 0, .heap_mask = 0,
.flags = {}, .flags = {},
@ -109,7 +119,10 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce
.kind = 0, .kind = 0,
.address = GetInteger(buffer), .address = GetInteger(buffer),
}; };
R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{};
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,18 +39,6 @@ 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) {
@ -328,12 +316,22 @@ 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), optval.size()); static_cast<u32>(optname), optlen);
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval));
} }
void BSD::Shutdown(HLERequestContext& ctx) { void BSD::Shutdown(HLERequestContext& ctx) {
@ -523,19 +521,18 @@ 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(), nfds * sizeof(PollFD)); std::memcpy(fds.data(), read_buffer.data(), length);
if (timeout >= 0) { if (timeout >= 0) {
const s64 seconds = timeout / 1000; const s64 seconds = timeout / 1000;
@ -583,7 +580,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(), nfds * sizeof(PollFD)); std::memcpy(write_buffer.data(), fds.data(), length);
return Translate(result); return Translate(result);
} }
@ -611,7 +608,8 @@ 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);
PutValue(write_buffer, guest_addr_in); const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
std::memcpy(write_buffer.data(), &guest_addr_in, length);
return {new_fd, Errno::SUCCESS}; return {new_fd, Errno::SUCCESS};
} }
@ -621,7 +619,8 @@ 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));
auto addr_in = GetValue<SockAddrIn>(addr); SockAddrIn addr_in;
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)));
} }
@ -632,7 +631,8 @@ Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
} }
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
auto addr_in = GetValue<SockAddrIn>(addr); SockAddrIn addr_in;
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));
PutValue(write_buffer, guest_addrin); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(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));
PutValue(write_buffer, guest_addrin); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(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));
PutValue(optval, translated_pending_err); memcpy(optval.data(), &translated_pending_err, sizeof(Errno));
} }
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, std::span<const u8> optval) { Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@ -748,15 +748,17 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8
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(optval.size() == sizeof(Linger)); ASSERT(optlen == sizeof(Linger));
auto linger = GetValue<Linger>(optval); Linger linger;
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(optval.size() == sizeof(u32)); ASSERT(optlen == sizeof(u32));
auto value = GetValue<u32>(optval); u32 value;
std::memcpy(&value, optval, sizeof(value));
switch (optname) { switch (optname) {
case OptName::REUSEADDR: case OptName::REUSEADDR:
@ -860,7 +862,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);
PutValue(addr, result); std::memcpy(addr.data(), &result, sizeof(result));
} }
} }
@ -884,7 +886,8 @@ 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));
auto guest_addr_in = GetValue<SockAddrIn>(addr); SockAddrIn guest_addr_in;
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, std::span<const u8> optval); Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* 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,7 +3,6 @@
#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"
@ -100,14 +99,8 @@ 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) {}
CreateChannel(channel); void RasterizerNull::BindChannel(Tegra::Control::ChannelState& 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,56 +137,6 @@ 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();
@ -404,10 +354,17 @@ 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) {
const auto crop_rect = NormalizeCrop(framebuffer, screen_info); auto crop_rect = framebuffer.crop_rect;
const VkExtent2D fsr_input_size{ if (crop_rect.GetWidth() == 0) {
.width = Settings::values.resolution_info.ScaleUp(screen_info.width), crop_rect.right = framebuffer.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);
@ -1440,37 +1397,61 @@ 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 {
f32 left, top, right, bottom; const auto& framebuffer_transform_flags = framebuffer.transform_flags;
const auto& framebuffer_crop_rect = framebuffer.crop_rect;
if (fsr) { static constexpr Common::Rectangle<f32> texcoords{0.f, 0.f, 1.f, 1.f};
// FSR has already applied the crop, so we just want to render the image auto left = texcoords.left;
// it has produced. auto right = texcoords.right;
left = 0;
top = 0;
right = 1;
bottom = 1;
} else {
// Get the normalized crop rectangle.
const auto crop = NormalizeCrop(framebuffer, screen_info);
// Apply the crop. switch (framebuffer_transform_flags) {
left = crop.left; case Service::android::BufferTransformFlags::Unset:
top = crop.top; break;
right = crop.right; case Service::android::BufferTransformFlags::FlipV:
bottom = crop.bottom; // Flip the framebuffer vertically
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[0] = ScreenRectVertex(x, y, left, top); data.vertices[1] =
data.vertices[1] = ScreenRectVertex(x + w, y, right, top); ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left_start + left * scale_v);
data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom); data.vertices[2] =
data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); ScreenRectVertex(x, y + h, texcoords.top * scale_u, left_start + right * scale_v);
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<f32>& crop_rect) { VkExtent2D input_image_extent, const Common::Rectangle<int>& crop_rect) {
UpdateDescriptorSet(image_index, image_view); UpdateDescriptorSet(image_index, image_view);
@ -61,21 +61,15 @@ 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);
const f32 input_image_width = static_cast<f32>(input_image_extent.width);
const f32 input_image_height = static_cast<f32>(input_image_extent.height);
const f32 output_image_width = static_cast<f32>(output_size.width);
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;
std::array<u32, 4 * 4> push_constants; std::array<u32, 4 * 4> push_constants;
FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4, FsrEasuConOffset(
push_constants.data() + 8, push_constants.data() + 12, push_constants.data() + 0, push_constants.data() + 4, push_constants.data() + 8,
push_constants.data() + 12,
viewport_width, viewport_height, input_image_width, input_image_height, static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
output_image_width, output_image_height, viewport_x, viewport_y); static_cast<f32>(input_image_extent.width), static_cast<f32>(input_image_extent.height),
static_cast<f32>(output_size.width), static_cast<f32>(output_size.height),
static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
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<f32>& crop_rect); VkExtent2D input_image_extent, const Common::Rectangle<int>& 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 += conv(static_cast<f32>(regs.surface_clip.height)); y += 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(hid_core, this); advanced = new ConfigureInputAdvanced(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,13 +4,11 @@
#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(Core::HID::HIDCore& hid_core_, QWidget* parent) ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} { : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
ui->setupUi(this); ui->setupUi(this);
controllers_color_buttons = {{ controllers_color_buttons = {{
@ -125,8 +123,6 @@ 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,15 +14,11 @@ 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(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr); explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
~ConfigureInputAdvanced() override; ~ConfigureInputAdvanced() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -48,6 +44,4 @@ 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;
} }
// Profile image must be 256x256 // Some games crash when the profile image is too big. Resize any image bigger than 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::KeepAspectRatioByExpanding, Qt::SmoothTransformation); image = image.scaled(256, 256, Qt::KeepAspectRatio);
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,6 +156,7 @@ 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,10 +1908,7 @@ 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();
@ -2177,7 +2174,6 @@ void GMainWindow::ShutdownGame() {
return; return;
} }
play_time_manager->Stop();
OnShutdownBegin(); OnShutdownBegin();
OnEmulationStopTimeExpired(); OnEmulationStopTimeExpired();
OnEmulationStopped(); OnEmulationStopped();
@ -2741,7 +2737,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return; return;
} }
const auto extracted = FileSys::ExtractRomFS(romfs); const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
if (extracted == nullptr) { if (extracted == nullptr) {
failed(); failed();
return; return;
@ -3488,7 +3484,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
} }
void GMainWindow::OnExit() { void GMainWindow::OnExit() {
ShutdownGame(); OnStopGame();
} }
void GMainWindow::OnSaveConfig() { void GMainWindow::OnSaveConfig() {
@ -4276,7 +4272,7 @@ void GMainWindow::OnToggleStatusBar() {
} }
void GMainWindow::OnAlbum() { void GMainWindow::OnAlbum() {
constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer); constexpr u64 AlbumId = 0x010000000000100Dull;
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"),
@ -4299,7 +4295,7 @@ void GMainWindow::OnAlbum() {
} }
void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet); constexpr u64 CabinetId = 0x0100000000001002ull;
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"),
@ -4323,7 +4319,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
} }
void GMainWindow::OnMiiEdit() { void GMainWindow::OnMiiEdit() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); constexpr u64 MiiEditId = 0x0100000000001009ull;
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"),
@ -4851,12 +4847,7 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe
} }
bool GMainWindow::ConfirmClose() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) {
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?");
@ -4961,7 +4952,7 @@ bool GMainWindow::ConfirmChangeGame() {
} }
bool GMainWindow::ConfirmForceLockedExit() { bool GMainWindow::ConfirmForceLockedExit() {
if (emu_thread == nullptr) { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) {
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,6 +93,10 @@ 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",