diff options
122 files changed, 2000 insertions, 748 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d3146c9e..f5ef0ef50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,7 +489,7 @@ if (ENABLE_SDL2)      if (YUZU_USE_BUNDLED_SDL2)          # Detect toolchain and platform          if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64) -            set(SDL2_VER "SDL2-2.0.18") +            set(SDL2_VER "SDL2-2.28.0")          else()              message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")          endif() @@ -509,7 +509,7 @@ if (ENABLE_SDL2)      elseif (YUZU_USE_EXTERNAL_SDL2)          message(STATUS "Using SDL2 from externals.")      else() -        find_package(SDL2 2.0.18 REQUIRED) +        find_package(SDL2 2.26.4 REQUIRED)      endif()  endif() diff --git a/externals/SDL b/externals/SDL -Subproject f17058b562c8a1090c0c996b42982721ace9090 +Subproject ffa78e6bead23e2ba3adf8ec2367ff2218d4343 diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index f0a6753a9..b1771b424 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -27,13 +27,13 @@ import android.view.MotionEvent  import android.view.Surface  import android.view.View  import android.view.inputmethod.InputMethodManager +import android.widget.Toast  import androidx.activity.viewModels  import androidx.appcompat.app.AppCompatActivity  import androidx.core.view.WindowCompat  import androidx.core.view.WindowInsetsCompat  import androidx.core.view.WindowInsetsControllerCompat  import androidx.navigation.fragment.NavHostFragment -import kotlin.math.roundToInt  import org.yuzu.yuzu_emu.NativeLibrary  import org.yuzu.yuzu_emu.R  import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding @@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game  import org.yuzu.yuzu_emu.utils.ControllerMappingHelper  import org.yuzu.yuzu_emu.utils.ForegroundService  import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.MemoryUtil  import org.yuzu.yuzu_emu.utils.NfcReader  import org.yuzu.yuzu_emu.utils.ThemeHelper +import kotlin.math.roundToInt  class EmulationActivity : AppCompatActivity(), SensorEventListener {      private lateinit var binding: ActivityEmulationBinding @@ -102,6 +104,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {          inputHandler = InputHandler()          inputHandler.initialize() +        val memoryUtil = MemoryUtil(this) +        if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { +            Toast.makeText( +                this, +                getString( +                    R.string.device_memory_inadequate, +                    memoryUtil.getDeviceRAM(), +                    "8 ${getString(R.string.memory_gigabyte)}" +                ), +                Toast.LENGTH_LONG +            ).show() +        } +          // Start a foreground service to prevent the app from getting killed in the background          val startIntent = Intent(this, ForegroundService::class.java)          startForegroundService(startIntent) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt new file mode 100644 index 000000000..b29b627e9 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.fragments + +import android.app.Dialog +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R + +class LongMessageDialogFragment : DialogFragment() { +    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { +        val titleId = requireArguments().getInt(TITLE) +        val description = requireArguments().getString(DESCRIPTION) +        val helpLinkId = requireArguments().getInt(HELP_LINK) + +        val dialog = MaterialAlertDialogBuilder(requireContext()) +            .setPositiveButton(R.string.close, null) +            .setTitle(titleId) +            .setMessage(description) + +        if (helpLinkId != 0) { +            dialog.setNeutralButton(R.string.learn_more) { _, _ -> +                openLink(getString(helpLinkId)) +            } +        } + +        return dialog.show() +    } + +    private fun openLink(link: String) { +        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) +        startActivity(intent) +    } + +    companion object { +        const val TAG = "LongMessageDialogFragment" + +        private const val TITLE = "Title" +        private const val DESCRIPTION = "Description" +        private const val HELP_LINK = "Link" + +        fun newInstance( +            titleId: Int, +            description: String, +            helpLinkId: Int = 0 +        ): LongMessageDialogFragment { +            val dialog = LongMessageDialogFragment() +            val bundle = Bundle() +            bundle.apply { +                putInt(TITLE, titleId) +                putString(DESCRIPTION, description) +                putInt(HELP_LINK, helpLinkId) +            } +            dialog.arguments = bundle +            return dialog +        } +    } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index cc1d87f1b..3086cfad3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -4,6 +4,7 @@  package org.yuzu.yuzu_emu.ui.main  import android.content.Intent +import android.net.Uri  import android.os.Bundle  import android.view.View  import android.view.ViewGroup.MarginLayoutParams @@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel  import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity  import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile  import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment +import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment  import org.yuzu.yuzu_emu.fragments.MessageDialogFragment  import org.yuzu.yuzu_emu.model.GamesViewModel  import org.yuzu.yuzu_emu.model.HomeViewModel @@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {              }          } -    val installGameUpdate = -        registerForActivityResult(ActivityResultContracts.OpenDocument()) { -            if (it == null) { -                return@registerForActivityResult -            } - +    val installGameUpdate = registerForActivityResult( +        ActivityResultContracts.OpenMultipleDocuments() +    ) { documents: List<Uri> -> +        if (documents.isNotEmpty()) {              IndeterminateProgressDialogFragment.newInstance(                  this@MainActivity,                  R.string.install_game_content              ) { -                val result = NativeLibrary.installFileToNand(it.toString()) +                var installSuccess = 0 +                var installOverwrite = 0 +                var errorBaseGame = 0 +                var errorExtension = 0 +                var errorOther = 0 +                var errorTotal = 0                  lifecycleScope.launch { -                    withContext(Dispatchers.Main) { -                        when (result) { +                    documents.forEach { +                        when (NativeLibrary.installFileToNand(it.toString())) {                              NativeLibrary.InstallFileToNandResult.Success -> { -                                Toast.makeText( -                                    applicationContext, -                                    R.string.install_game_content_success, -                                    Toast.LENGTH_SHORT -                                ).show() +                                installSuccess += 1                              }                              NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> { -                                Toast.makeText( -                                    applicationContext, -                                    R.string.install_game_content_success_overwrite, -                                    Toast.LENGTH_SHORT -                                ).show() +                                installOverwrite += 1                              }                              NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> { -                                MessageDialogFragment.newInstance( -                                    R.string.install_game_content_failure, -                                    R.string.install_game_content_failure_base -                                ).show(supportFragmentManager, MessageDialogFragment.TAG) +                                errorBaseGame += 1                              }                              NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> { -                                MessageDialogFragment.newInstance( -                                    R.string.install_game_content_failure, -                                    R.string.install_game_content_failure_file_extension, -                                    R.string.install_game_content_help_link -                                ).show(supportFragmentManager, MessageDialogFragment.TAG) +                                errorExtension += 1                              }                              else -> { -                                MessageDialogFragment.newInstance( -                                    R.string.install_game_content_failure, -                                    R.string.install_game_content_failure_description, -                                    R.string.install_game_content_help_link -                                ).show(supportFragmentManager, MessageDialogFragment.TAG) +                                errorOther += 1                              }                          }                      } +                    withContext(Dispatchers.Main) { +                        val separator = System.getProperty("line.separator") ?: "\n" +                        val installResult = StringBuilder() +                        if (installSuccess > 0) { +                            installResult.append( +                                getString( +                                    R.string.install_game_content_success_install, +                                    installSuccess +                                ) +                            ) +                            installResult.append(separator) +                        } +                        if (installOverwrite > 0) { +                            installResult.append( +                                getString( +                                    R.string.install_game_content_success_overwrite, +                                    installOverwrite +                                ) +                            ) +                            installResult.append(separator) +                        } +                        errorTotal = errorBaseGame + errorExtension + errorOther +                        if (errorTotal > 0) { +                            installResult.append(separator) +                            installResult.append( +                                getString( +                                    R.string.install_game_content_failed_count, +                                    errorTotal +                                ) +                            ) +                            installResult.append(separator) +                            if (errorBaseGame > 0) { +                                installResult.append(separator) +                                installResult.append( +                                    getString(R.string.install_game_content_failure_base) +                                ) +                                installResult.append(separator) +                            } +                            if (errorExtension > 0) { +                                installResult.append(separator) +                                installResult.append( +                                    getString(R.string.install_game_content_failure_file_extension) +                                ) +                                installResult.append(separator) +                            } +                            if (errorOther > 0) { +                                installResult.append( +                                    getString(R.string.install_game_content_failure_description) +                                ) +                                installResult.append(separator) +                            } +                            LongMessageDialogFragment.newInstance( +                                R.string.install_game_content_failure, +                                installResult.toString().trim(), +                                R.string.install_game_content_help_link +                            ).show(supportFragmentManager, LongMessageDialogFragment.TAG) +                        } else { +                            LongMessageDialogFragment.newInstance( +                                R.string.install_game_content_success, +                                installResult.toString().trim() +                            ).show(supportFragmentManager, LongMessageDialogFragment.TAG) +                        } +                    }                  } -                return@newInstance result +                return@newInstance installSuccess + installOverwrite + errorTotal              }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)          } +    }  } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt new file mode 100644 index 000000000..18e5fa0b0 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.utils + +import android.app.ActivityManager +import android.content.Context +import org.yuzu.yuzu_emu.R +import java.util.Locale + +class MemoryUtil(val context: Context) { + +    private val Long.floatForm: String +        get() = String.format(Locale.ROOT, "%.2f", this.toDouble()) + +    private fun bytesToSizeUnit(size: Long): String { +        return when { +            size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" +            size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}" +            size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}" +            size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}" +            size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}" +            size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}" +            else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}" +        } +    } + +    private val totalMemory = +        with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { +            val memInfo = ActivityManager.MemoryInfo() +            getMemoryInfo(memInfo) +            memInfo.totalMem +        } + +    fun isLessThan(minimum: Int, size: Long): Boolean { +        return when (size) { +            Kb -> totalMemory < Mb && totalMemory < minimum +            Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum +            Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum +            Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum +            Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum +            Eb -> totalMemory / Eb < minimum +            else -> totalMemory < Kb && totalMemory < minimum +        } +    } + +    fun getDeviceRAM(): String { +        return bytesToSizeUnit(totalMemory) +    } + +    companion object { +        const val Kb: Long = 1024 +        const val Mb = Kb * 1024 +        const val Gb = Mb * 1024 +        const val Tb = Gb * 1024 +        const val Pb = Tb * 1024 +        const val Eb = Pb * 1024 +    } +} diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 632aa50b3..f4fed0886 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -237,6 +237,7 @@ public:          m_software_keyboard = android_keyboard.get();          m_system.SetShuttingDown(false);          m_system.ApplySettings(); +        Settings::LogSettings();          m_system.HIDCore().ReloadInputDevices();          m_system.SetAppletFrontendSet({              nullptr,                     // Amiibo Settings diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index cc1d8c39d..21805d274 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -104,12 +104,14 @@      <string name="share_log_missing">No log file found</string>      <string name="install_game_content">Install game content</string>      <string name="install_game_content_description">Install game updates or DLC</string> -    <string name="install_game_content_failure">Error installing file to NAND</string> -    <string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string> -    <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string> -    <string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string> -    <string name="install_game_content_success">Game content installed successfully</string> -    <string name="install_game_content_success_overwrite">Game content was overwritten successfully</string> +    <string name="install_game_content_failure">Error installing file(s) to NAND</string> +    <string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string> +    <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string> +    <string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string> +    <string name="install_game_content_failed_count">%1$d installation error(s)</string> +    <string name="install_game_content_success">Game content(s) installed successfully</string> +    <string name="install_game_content_success_install">%1$d installed successfully</string> +    <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>      <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>      <!-- About screen strings --> @@ -270,6 +272,7 @@      <string name="fatal_error">Fatal Error</string>      <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>      <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> +    <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>      <!-- Region Names -->      <string name="region_japan">Japan</string> @@ -300,6 +303,15 @@      <string name="language_traditional_chinese">Traditional Chinese (æ£é«”䏿–‡)</string>      <string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string> +    <!-- Memory Sizes --> +    <string name="memory_byte">Byte</string> +    <string name="memory_kilobyte">KB</string> +    <string name="memory_megabyte">MB</string> +    <string name="memory_gigabyte">GB</string> +    <string name="memory_terabyte">TB</string> +    <string name="memory_petabyte">PB</string> +    <string name="memory_exabyte">EB</string> +      <!-- Renderer APIs -->      <string name="renderer_vulkan">Vulkan</string>      <string name="renderer_none">None</string> diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 15082f6c6..5d8ed0ef7 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -7,6 +7,7 @@  #include <mutex>  #include <span>  #include <vector> +#include <boost/container/static_vector.hpp>  #include "audio_buffer.h"  #include "audio_core/device/device_session.h" @@ -48,7 +49,7 @@ public:       *       * @param out_buffers - The buffers which were registered.       */ -    void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) { +    void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) {          std::scoped_lock l{lock};          const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),                                         BufferAppendLimit - registered_count)}; @@ -162,7 +163,8 @@ public:       * @param max_buffers     - Maximum number of buffers to released.       * @return The number of buffers released.       */ -    u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) { +    u32 GetRegisteredAppendedBuffers( +        boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) {          std::scoped_lock l{lock};          if (registered_count + appended_count == 0) {              return 0; @@ -270,7 +272,7 @@ public:       */      bool FlushBuffers(u32& buffers_released) {          std::scoped_lock l{lock}; -        std::vector<AudioBuffer> buffers_flushed{}; +        boost::container::static_vector<AudioBuffer, N> buffers_flushed{};          buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit); diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index b5c0ef0e6..86811fcb8 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() {      }  } -void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { +void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {      for (const auto& buffer : buffers) {          Sink::SinkBuffer new_buffer{              .frames = buffer.size / (channel_count * sizeof(s16)), @@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {              .consumed = false,          }; +        tmp_samples.resize_destructive(buffer.size / sizeof(s16));          if (type == Sink::StreamType::In) { -            std::vector<s16> samples{}; -            stream->AppendBuffer(new_buffer, samples); +            stream->AppendBuffer(new_buffer, tmp_samples);          } else { -            std::vector<s16> samples(buffer.size / sizeof(s16)); -            system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size); -            stream->AppendBuffer(new_buffer, samples); +            system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(), +                                                       buffer.size); +            stream->AppendBuffer(new_buffer, tmp_samples);          }      }  } diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 75f766c68..7d52f362d 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -10,6 +10,7 @@  #include "audio_core/common/common.h"  #include "audio_core/sink/sink.h" +#include "common/scratch_buffer.h"  #include "core/hle/service/audio/errors.h"  namespace Core { @@ -62,7 +63,7 @@ public:       *       * @param buffers - The buffers to play.       */ -    void AppendBuffers(std::span<const AudioBuffer> buffers) const; +    void AppendBuffers(std::span<const AudioBuffer> buffers);      /**       * (Audio In only) Pop samples from the backend, and write them back to this buffer's address. @@ -146,8 +147,8 @@ private:      std::shared_ptr<Core::Timing::EventType> thread_event;      /// Is this session initialised?      bool initialized{}; -    /// Buffer queue -    std::vector<AudioBuffer> buffer_queue{}; +    /// Temporary sample buffer +    Common::ScratchBuffer<s16> tmp_samples{};  };  } // namespace AudioCore diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index e23e51758..579129121 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -2,6 +2,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include <mutex> +  #include "audio_core/audio_event.h"  #include "audio_core/audio_manager.h"  #include "audio_core/in/audio_in_system.h" @@ -89,7 +90,7 @@ Result System::Start() {      session->Start();      state = State::Started; -    std::vector<AudioBuffer> buffers_to_flush{}; +    boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};      buffers.RegisterBuffers(buffers_to_flush);      session->AppendBuffers(buffers_to_flush);      session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); @@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {  void System::RegisterBuffers() {      if (state == State::Started) { -        std::vector<AudioBuffer> registered_buffers{}; +        boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};          buffers.RegisterBuffers(registered_buffers);          session->AppendBuffers(registered_buffers);      } diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index bd13f7219..0adf64bd3 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -89,7 +89,7 @@ Result System::Start() {      session->Start();      state = State::Started; -    std::vector<AudioBuffer> buffers_to_flush{}; +    boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};      buffers.RegisterBuffers(buffers_to_flush);      session->AppendBuffers(buffers_to_flush);      session->SetRingSize(static_cast<u32>(buffers_to_flush.size())); @@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {  void System::RegisterBuffers() {      if (state == State::Started) { -        std::vector<AudioBuffer> registered_buffers{}; +        boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};          buffers.RegisterBuffers(registered_buffers);          session->AppendBuffers(registered_buffers);      } diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index ff5d31bd6..f45933203 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -8,6 +8,7 @@  #include "audio_core/renderer/command/resample/resample.h"  #include "common/fixed_point.h"  #include "common/logging/log.h" +#include "common/scratch_buffer.h"  #include "core/memory.h"  namespace AudioCore::AudioRenderer { @@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};  template <typename T>  static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,                       const DecodeArg& req) { +    std::array<T, TempBufferSize> tmp_samples{};      constexpr s32 min{std::numeric_limits<s16>::min()};      constexpr s32 max{std::numeric_limits<s16>::max()}; @@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,          const u64 size{channel_count * samples_to_decode};          const u64 size_bytes{size * sizeof(T)}; -        std::vector<T> samples(size); -        memory.ReadBlockUnsafe(source, samples.data(), size_bytes); +        memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);          if constexpr (std::is_floating_point_v<T>) {              for (u32 i = 0; i < samples_to_decode; i++) { -                auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * +                auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *                                               std::numeric_limits<s16>::max())};                  out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));              }          } else {              for (u32 i = 0; i < samples_to_decode; i++) { -                out_buffer[i] = samples[i * channel_count + req.target_channel]; +                out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];              }          }      } break; @@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,          }          const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; -        std::vector<T> samples(samples_to_decode); -        memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T)); +        memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));          if constexpr (std::is_floating_point_v<T>) {              for (u32 i = 0; i < samples_to_decode; i++) { -                auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * +                auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *                                               std::numeric_limits<s16>::max())};                  out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));              }          } else { -            std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16)); +            std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));          }          break;      } @@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,   */  static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,                         const DecodeArg& req) { +    std::array<u8, TempBufferSize> wavebuffer{};      constexpr u32 SamplesPerFrame{14};      constexpr u32 NibblesPerFrame{16}; @@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,      }      const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; -    std::vector<u8> wavebuffer(size); -    memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), -                           wavebuffer.size()); +    memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);      auto context{req.adpcm_context};      auto header{context->header}; @@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf      u32 offset{voice_state.offset};      auto output_buffer{args.output}; -    std::vector<s16> temp_buffer(TempBufferSize, 0); +    std::array<s16, TempBufferSize> temp_buffer{};      while (remaining_sample_count > 0) {          const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)}; diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp index 7229618e8..ee9b68d5b 100644 --- a/src/audio_core/renderer/command/effect/compressor.cpp +++ b/src/audio_core/renderer/command/effect/compressor.cpp @@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2&  static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,                                    CompressorInfo::State& state, bool enabled, -                                  std::vector<std::span<const s32>> input_buffers, -                                  std::vector<std::span<s32>> output_buffers, u32 sample_count) { +                                  std::span<std::span<const s32>> input_buffers, +                                  std::span<std::span<s32>> output_buffers, u32 sample_count) {      if (enabled) {          auto state_00{state.unk_00};          auto state_04{state.unk_04}; @@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&  }  void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (s16 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp index a4e408d40..e536cbb1e 100644 --- a/src/audio_core/renderer/command/effect/delay.cpp +++ b/src/audio_core/renderer/command/effect/delay.cpp @@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,          state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();          state.delay_lines[channel].sample_count = sample_count.to_int_floor();          state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0); -        if (state.delay_lines[channel].buffer.size() == 0) { +        if (state.delay_lines[channel].sample_count == 0) {              state.delay_lines[channel].buffer.push_back(0);          }          state.delay_lines[channel].buffer_pos = 0; @@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,   */  template <size_t NumChannels>  static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, -                       std::vector<std::span<const s32>>& inputs, -                       std::vector<std::span<s32>>& outputs, const u32 sample_count) { +                       std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs, +                       const u32 sample_count) {      for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {          std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};          for (u32 channel = 0; channel < NumChannels; channel++) { @@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St   * @param sample_count - Number of samples to process.   */  static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, -                             const bool enabled, std::vector<std::span<const s32>>& inputs, -                             std::vector<std::span<s32>>& outputs, const u32 sample_count) { +                             const bool enabled, std::span<std::span<const s32>> inputs, +                             std::span<std::span<s32>> outputs, const u32 sample_count) {      if (!IsChannelCountValid(params.channel_count)) {          LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count); @@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce  }  void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (s16 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index 27d8b9844..d2bfb67cc 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp @@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&  }  void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (u32 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp index e8fb0e2fc..4161a9821 100644 --- a/src/audio_core/renderer/command/effect/light_limiter.cpp +++ b/src/audio_core/renderer/command/effect/light_limiter.cpp @@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio   */  static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,                                      LightLimiterInfo::State& state, const bool enabled, -                                    std::vector<std::span<const s32>>& inputs, -                                    std::vector<std::span<s32>>& outputs, const u32 sample_count, +                                    std::span<std::span<const s32>> inputs, +                                    std::span<std::span<s32>> outputs, const u32 sample_count,                                      LightLimiterInfo::StatisticsInternal* statistics) {      constexpr s64 min{std::numeric_limits<s32>::min()};      constexpr s64 max{std::numeric_limits<s32>::max()}; @@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP  }  void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (u32 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, @@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP  }  void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (u32 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index 8b9b65214..fc2f15a5e 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&   */  template <size_t NumChannels>  static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, -                              std::vector<std::span<const s32>>& inputs, -                              std::vector<std::span<s32>>& outputs, const u32 sample_count) { +                              std::span<std::span<const s32>> inputs, +                              std::span<std::span<s32>> outputs, const u32 sample_count) {      static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{          0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      }; @@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever   * @param sample_count - Number of samples to process.   */  static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, -                              const bool enabled, std::vector<std::span<const s32>>& inputs, -                              std::vector<std::span<s32>>& outputs, const u32 sample_count) { +                              const bool enabled, std::span<std::span<const s32>> inputs, +                              std::span<std::span<s32>> outputs, const u32 sample_count) {      if (enabled) {          switch (params.channel_count) {          case 0: @@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc  }  void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { -    std::vector<std::span<const s32>> input_buffers(parameter.channel_count); -    std::vector<std::span<s32>> output_buffers(parameter.channel_count); +    std::array<std::span<const s32>, MaxChannels> input_buffers{}; +    std::array<std::span<s32>, MaxChannels> output_buffers{};      for (u32 i = 0; i < parameter.channel_count; i++) {          input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp index ded5afc94..e2ce59792 100644 --- a/src/audio_core/renderer/command/sink/circular_buffer.cpp +++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp @@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces      constexpr s32 min{std::numeric_limits<s16>::min()};      constexpr s32 max{std::numeric_limits<s16>::max()}; -    std::vector<s16> output(processor.sample_count); +    std::array<s16, TargetSampleCount * MaxChannels> output{};      for (u32 channel = 0; channel < input_count; channel++) {          auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,                                                   processor.sample_count)}; @@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces          }          processor.memory->WriteBlockUnsafe(address + pos, output.data(), -                                           output.size() * sizeof(s16)); +                                           processor.sample_count * sizeof(s16));          pos += static_cast<u32>(processor.sample_count * sizeof(s16));          if (pos >= size) {              pos = 0; diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp index e88372a75..5f74dd7ad 100644 --- a/src/audio_core/renderer/command/sink/device.cpp +++ b/src/audio_core/renderer/command/sink/device.cpp @@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {          .consumed{false},      }; -    std::vector<s16> samples(out_buffer.frames * input_count); - +    std::array<s16, TargetSampleCount * MaxChannels> samples{};      for (u32 channel = 0; channel < input_count; channel++) {          const auto offset{inputs[channel] * out_buffer.frames}; @@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {      }      out_buffer.tag = reinterpret_cast<u64>(samples.data()); -    stream->AppendBuffer(out_buffer, samples); +    stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count});      if (stream->IsPaused()) {          stream->Start(); diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 35b748ede..3a18ae7c2 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp @@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) {          return false;      } -    std::vector<s32> sorted_results{node_states.GetSortedResuls()}; -    const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))}; +    auto sorted_results{node_states.GetSortedResuls()}; +    const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))};      for (s32 i = 0; i < result_size; i++) { -        sorted_mix_infos[i] = &mix_infos[sorted_results[i]]; +        sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]];      }      CalcMixBufferOffset(); diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp index 1821a51e6..b7a44a54c 100644 --- a/src/audio_core/renderer/nodes/node_states.cpp +++ b/src/audio_core/renderer/nodes/node_states.cpp @@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const {      return node_count;  } -std::vector<s32> NodeStates::GetSortedResuls() const { -    return {results.rbegin(), results.rbegin() + result_pos}; +std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const { +    return {results.rbegin(), result_pos};  }  } // namespace AudioCore::AudioRenderer diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h index 94b1d1254..e768cd4b5 100644 --- a/src/audio_core/renderer/nodes/node_states.h +++ b/src/audio_core/renderer/nodes/node_states.h @@ -175,7 +175,7 @@ public:       *       * @return Vector of nodes in reverse order.       */ -    std::vector<s32> GetSortedResuls() const; +    std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const;  private:      /// Number of nodes in the graph diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index 53b258c4f..a23627472 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:      std::scoped_lock l{lock};      const auto start_time{core.CoreTiming().GetClockTicks()}; +    std::memset(output.data(), 0, output.size());      InfoUpdater info_updater(input, output, process_handle, behavior); diff --git a/src/audio_core/sink/null_sink.h b/src/audio_core/sink/null_sink.h index 1215d3cd2..b6b43c93e 100644 --- a/src/audio_core/sink/null_sink.h +++ b/src/audio_core/sink/null_sink.h @@ -20,7 +20,7 @@ public:      explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)          : SinkStream{system_, type_} {}      ~NullSinkStreamImpl() override {} -    void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {} +    void AppendBuffer(SinkBuffer&, std::span<s16>) override {}      std::vector<s16> ReleaseBuffer(u64) override {          return {};      } diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 9a718a9cc..404dcd0e9 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -18,7 +18,7 @@  namespace AudioCore::Sink { -void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) { +void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {      if (type == StreamType::In) {          queue.enqueue(buffer);          queued_buffers++; @@ -66,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {                  static_cast<s16>(std::clamp(right_sample, min, max));          } -        samples.resize(samples.size() / system_channels * device_channels); +        samples = samples.subspan(0, samples.size() / system_channels * device_channels);      } else if (system_channels == 2 && device_channels == 6) {          // We need moar samples! Not all games will provide 6 channel audio.          // TODO: Implement some upmixing here. Currently just passthrough, with other          // channels left as silence. -        std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0); +        auto new_size = samples.size() / system_channels * device_channels; +        tmp_samples.resize_destructive(new_size); -        for (u32 read_index = 0, write_index = 0; read_index < samples.size(); +        for (u32 read_index = 0, write_index = 0; read_index < new_size;               read_index += system_channels, write_index += device_channels) {              const auto left_sample{static_cast<s16>(std::clamp(                  static_cast<s32>( @@ -82,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {                      volume),                  min, max))}; -            new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample; +            tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;              const auto right_sample{static_cast<s16>(std::clamp(                  static_cast<s32>( @@ -90,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {                      volume),                  min, max))}; -            new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample; +            tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;          } -        samples = std::move(new_samples); +        samples = std::span<s16>(tmp_samples);      } else if (volume != 1.0f) {          for (u32 i = 0; i < samples.size(); i++) { diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 41cbadc9c..98d72ace1 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h @@ -16,6 +16,7 @@  #include "common/polyfill_thread.h"  #include "common/reader_writer_queue.h"  #include "common/ring_buffer.h" +#include "common/scratch_buffer.h"  #include "common/thread.h"  namespace Core { @@ -170,7 +171,7 @@ public:       * @param buffer  - Audio buffer information to be queued.       * @param samples - The s16 samples to be queue for playback.       */ -    virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples); +    virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);      /**       * Release a buffer. Audio In only, will fill a buffer with recorded samples. @@ -255,6 +256,8 @@ private:      /// Signalled when ring buffer entries are consumed      std::condition_variable_any release_cv;      std::mutex release_mutex; +    /// Temporary buffer for appending samples when upmixing +    Common::ScratchBuffer<s16> tmp_samples{};  };  using SinkStreamPtr = std::unique_ptr<SinkStream>; diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 6d66c926d..1baf6d746 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp @@ -436,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable          if (True(filter & DirEntryFilter::File) &&              entry.status().type() == fs::file_type::regular) { -            if (!callback(entry.path())) { +            if (!callback(entry)) {                  callback_error = true;                  break;              } @@ -444,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable          if (True(filter & DirEntryFilter::Directory) &&              entry.status().type() == fs::file_type::directory) { -            if (!callback(entry.path())) { +            if (!callback(entry)) {                  callback_error = true;                  break;              } @@ -493,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,          if (True(filter & DirEntryFilter::File) &&              entry.status().type() == fs::file_type::regular) { -            if (!callback(entry.path())) { +            if (!callback(entry)) {                  callback_error = true;                  break;              } @@ -501,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,          if (True(filter & DirEntryFilter::Directory) &&              entry.status().type() == fs::file_type::directory) { -            if (!callback(entry.path())) { +            if (!callback(entry)) {                  callback_error = true;                  break;              } diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h index 5a4090c19..900f85d24 100644 --- a/src/common/fs/fs_types.h +++ b/src/common/fs/fs_types.h @@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);   * @returns A boolean value.   *          Return true to indicate whether the callback is successful, false otherwise.   */ -using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; +using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;  } // namespace Common::FS diff --git a/src/common/input.h b/src/common/input.h index 66fb15f0a..ea30770ae 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -86,7 +86,7 @@ enum class NfcState {      NewAmiibo,      WaitingForAmiibo,      AmiiboRemoved, -    NotAnAmiibo, +    InvalidTagType,      NotSupported,      WrongDeviceState,      WriteFailed, @@ -218,8 +218,22 @@ struct CameraStatus {  };  struct NfcStatus { -    NfcState state{}; -    std::vector<u8> data{}; +    NfcState state{NfcState::Unknown}; +    u8 uuid_length; +    u8 protocol; +    u8 tag_type; +    std::array<u8, 10> uuid; +}; + +struct MifareData { +    u8 command; +    u8 sector; +    std::array<u8, 0x6> key; +    std::array<u8, 0x10> data; +}; + +struct MifareRequest { +    std::array<MifareData, 0x10> data;  };  // List of buttons to be passed to Qt that can be translated @@ -294,7 +308,7 @@ struct CallbackStatus {      BatteryStatus battery_status{};      VibrationStatus vibration_status{};      CameraFormat camera_status{CameraFormat::None}; -    NfcState nfc_status{NfcState::Unknown}; +    NfcStatus nfc_status{};      std::vector<u8> raw_data{};  }; @@ -356,9 +370,30 @@ public:          return NfcState::NotSupported;      } +    virtual NfcState StartNfcPolling() { +        return NfcState::NotSupported; +    } + +    virtual NfcState StopNfcPolling() { +        return NfcState::NotSupported; +    } + +    virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) { +        return NfcState::NotSupported; +    } +      virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {          return NfcState::NotSupported;      } + +    virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request, +                                    [[maybe_unused]] MifareRequest& out_data) { +        return NfcState::NotSupported; +    } + +    virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) { +        return NfcState::NotSupported; +    }  };  /// An abstract class template for a factory that can create input devices. diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 4c328ab44..416680d44 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -9,6 +9,7 @@  #include <cstddef>  #include <cstring>  #include <new> +#include <span>  #include <type_traits>  #include <vector> @@ -53,7 +54,7 @@ public:          return push_count;      } -    std::size_t Push(const std::vector<T>& input) { +    std::size_t Push(const std::span<T> input) {          return Push(input.data(), input.size());      } diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index a69a5a7af..6fe907953 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h @@ -3,6 +3,9 @@  #pragma once +#include <iterator> + +#include "common/concepts.h"  #include "common/make_unique_for_overwrite.h"  namespace Common { @@ -16,6 +19,12 @@ namespace Common {  template <typename T>  class ScratchBuffer {  public: +    using iterator = T*; +    using const_iterator = const T*; +    using value_type = T; +    using element_type = T; +    using iterator_category = std::contiguous_iterator_tag; +      ScratchBuffer() = default;      explicit ScratchBuffer(size_t initial_capacity) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 4e61d4335..d3286b352 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -153,7 +153,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {      const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);      std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; -    if (load_dir != nullptr && load_dir->GetSize() > 0) { +    if (load_dir != nullptr) {          const auto load_patch_dirs = load_dir->GetSubdirectories();          patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());      } @@ -354,8 +354,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t      const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);      const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);      if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || -        ((load_dir == nullptr || load_dir->GetSize() <= 0) && -         (sdmc_load_dir == nullptr || sdmc_load_dir->GetSize() <= 0))) { +        (load_dir == nullptr && sdmc_load_dir == nullptr)) {          return;      } @@ -496,7 +495,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u      // General Mods (LayeredFS and IPS)      const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); -    if (mod_dir != nullptr && mod_dir->GetSize() > 0) { +    if (mod_dir != nullptr) {          for (const auto& mod : mod_dir->GetSubdirectories()) {              std::string types; @@ -540,7 +539,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u      // SDMC mod directory (RomFS LayeredFS)      const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); -    if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { +    if (sdmc_mod_dir != nullptr) {          std::string types;          if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {              AppendCommaIfNotEmpty(types, "LayeredExeFS"); diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 7a15d8438..b0515ec05 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -10,6 +10,7 @@  #include "common/fs/fs.h"  #include "common/fs/path_util.h"  #include "common/logging/log.h" +#include "core/file_sys/vfs.h"  #include "core/file_sys/vfs_real.h"  // For FileTimeStampRaw @@ -72,8 +73,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {      return VfsEntryType::File;  } -VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, +                                                 Mode perms) {      const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); +    std::scoped_lock lk{list_lock};      if (auto it = cache.find(path); it != cache.end()) {          if (auto file = it->second.lock(); file) { @@ -81,23 +84,30 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {          }      } -    if (!FS::Exists(path) || !FS::IsFile(path)) { +    if (!size && !FS::IsFile(path)) {          return nullptr;      }      auto reference = std::make_unique<FileReference>(); -    this->InsertReferenceIntoList(*reference); +    this->InsertReferenceIntoListLocked(*reference); -    auto file = -        std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, std::move(reference), path, perms)); +    auto file = std::shared_ptr<RealVfsFile>( +        new RealVfsFile(*this, std::move(reference), path, perms, size));      cache[path] = file;      return file;  } +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +    return OpenFileFromEntry(path_, {}, perms); +} +  VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {      const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); -    cache.erase(path); +    { +        std::scoped_lock lk{list_lock}; +        cache.erase(path); +    }      // Current usages of CreateFile expect to delete the contents of an existing file.      if (FS::IsFile(path)) { @@ -127,8 +137,11 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_  VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {      const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);      const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); -    cache.erase(old_path); -    cache.erase(new_path); +    { +        std::scoped_lock lk{list_lock}; +        cache.erase(old_path); +        cache.erase(new_path); +    }      if (!FS::RenameFile(old_path, new_path)) {          return nullptr;      } @@ -137,7 +150,10 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_  bool RealVfsFilesystem::DeleteFile(std::string_view path_) {      const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); -    cache.erase(path); +    { +        std::scoped_lock lk{list_lock}; +        cache.erase(path); +    }      return FS::RemoveFile(path);  } @@ -176,14 +192,17 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {      return FS::RemoveDirRecursively(path);  } -void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms, -                                         FileReference& reference) { +std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, +                                                                 Mode perms, +                                                                 FileReference& reference) { +    std::unique_lock lk{list_lock}; +      // Temporarily remove from list. -    this->RemoveReferenceFromList(reference); +    this->RemoveReferenceFromListLocked(reference);      // Restore file if needed.      if (!reference.file) { -        this->EvictSingleReference(); +        this->EvictSingleReferenceLocked();          reference.file =              FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); @@ -193,12 +212,16 @@ void RealVfsFilesystem::RefreshReference(const std::string& path, Mode perms,      }      // Reinsert into list. -    this->InsertReferenceIntoList(reference); +    this->InsertReferenceIntoListLocked(reference); + +    return lk;  }  void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) { +    std::scoped_lock lk{list_lock}; +      // Remove from list. -    this->RemoveReferenceFromList(*reference); +    this->RemoveReferenceFromListLocked(*reference);      // Close the file.      if (reference->file) { @@ -207,14 +230,14 @@ void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference      }  } -void RealVfsFilesystem::EvictSingleReference() { +void RealVfsFilesystem::EvictSingleReferenceLocked() {      if (num_open_files < MaxOpenFiles || open_references.empty()) {          return;      }      // Get and remove from list.      auto& reference = open_references.back(); -    this->RemoveReferenceFromList(reference); +    this->RemoveReferenceFromListLocked(reference);      // Close the file.      if (reference.file) { @@ -223,10 +246,10 @@ void RealVfsFilesystem::EvictSingleReference() {      }      // Reinsert into closed list. -    this->InsertReferenceIntoList(reference); +    this->InsertReferenceIntoListLocked(reference);  } -void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) { +void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) {      if (reference.file) {          open_references.push_front(reference);      } else { @@ -234,7 +257,7 @@ void RealVfsFilesystem::InsertReferenceIntoList(FileReference& reference) {      }  } -void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) { +void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) {      if (reference.file) {          open_references.erase(open_references.iterator_to(reference));      } else { @@ -243,10 +266,10 @@ void RealVfsFilesystem::RemoveReferenceFromList(FileReference& reference) {  }  RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, -                         const std::string& path_, Mode perms_) +                         const std::string& path_, Mode perms_, std::optional<u64> size_)      : base(base_), reference(std::move(reference_)), path(path_),        parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)), -      perms(perms_) {} +      size(size_), perms(perms_) {}  RealVfsFile::~RealVfsFile() {      base.DropReference(std::move(reference)); @@ -257,12 +280,15 @@ std::string RealVfsFile::GetName() const {  }  std::size_t RealVfsFile::GetSize() const { -    base.RefreshReference(path, perms, *reference); -    return reference->file ? reference->file->GetSize() : 0; +    if (size) { +        return *size; +    } +    return FS::GetSize(path);  }  bool RealVfsFile::Resize(std::size_t new_size) { -    base.RefreshReference(path, perms, *reference); +    size.reset(); +    auto lk = base.RefreshReference(path, perms, *reference);      return reference->file ? reference->file->SetSize(new_size) : false;  } @@ -279,7 +305,7 @@ bool RealVfsFile::IsReadable() const {  }  std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { -    base.RefreshReference(path, perms, *reference); +    auto lk = base.RefreshReference(path, perms, *reference);      if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {          return 0;      } @@ -287,7 +313,8 @@ std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset)  }  std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { -    base.RefreshReference(path, perms, *reference); +    size.reset(); +    auto lk = base.RefreshReference(path, perms, *reference);      if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {          return 0;      } @@ -309,10 +336,11 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(      std::vector<VirtualFile> out; -    const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { -        const auto full_path_string = FS::PathToUTF8String(full_path); +    const FS::DirEntryCallable callback = [this, +                                           &out](const std::filesystem::directory_entry& entry) { +        const auto full_path_string = FS::PathToUTF8String(entry.path()); -        out.emplace_back(base.OpenFile(full_path_string, perms)); +        out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms));          return true;      }; @@ -330,8 +358,9 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi      std::vector<VirtualDir> out; -    const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { -        const auto full_path_string = FS::PathToUTF8String(full_path); +    const FS::DirEntryCallable callback = [this, +                                           &out](const std::filesystem::directory_entry& entry) { +        const auto full_path_string = FS::PathToUTF8String(entry.path());          out.emplace_back(base.OpenDirectory(full_path_string, perms)); @@ -483,12 +512,10 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()      std::map<std::string, VfsEntryType, std::less<>> out; -    const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) { -        const auto filename = FS::PathToUTF8String(full_path.filename()); - +    const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { +        const auto filename = FS::PathToUTF8String(entry.path().filename());          out.insert_or_assign(filename, -                             FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File); - +                             entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File);          return true;      }; diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index d8c900e33..26ea7df62 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -4,6 +4,8 @@  #pragma once  #include <map> +#include <mutex> +#include <optional>  #include <string_view>  #include "common/intrusive_list.h"  #include "core/file_sys/mode.h" @@ -20,6 +22,8 @@ struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {  };  class RealVfsFile; +class RealVfsDirectory; +  class RealVfsFilesystem : public VfsFilesystem {  public:      RealVfsFilesystem(); @@ -45,17 +49,24 @@ private:      std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;      ReferenceListType open_references;      ReferenceListType closed_references; +    std::mutex list_lock;      size_t num_open_files{};  private:      friend class RealVfsFile; -    void RefreshReference(const std::string& path, Mode perms, FileReference& reference); +    std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, +                                                  FileReference& reference);      void DropReference(std::unique_ptr<FileReference>&& reference); -    void EvictSingleReference();  private: -    void InsertReferenceIntoList(FileReference& reference); -    void RemoveReferenceFromList(FileReference& reference); +    friend class RealVfsDirectory; +    VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, +                                  Mode perms = Mode::Read); + +private: +    void EvictSingleReferenceLocked(); +    void InsertReferenceIntoListLocked(FileReference& reference); +    void RemoveReferenceFromListLocked(FileReference& reference);  };  // An implementation of VfsFile that represents a file on the user's computer. @@ -78,13 +89,14 @@ public:  private:      RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, -                const std::string& path, Mode perms = Mode::Read); +                const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {});      RealVfsFilesystem& base;      std::unique_ptr<FileReference> reference;      std::string path;      std::string parent_path;      std::vector<std::string> path_components; +    std::optional<u64> size;      Mode perms;  }; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 0a7777732..c937495f9 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -149,12 +149,16 @@ void EmulatedController::LoadDevices() {      camera_params[0] = right_joycon;      camera_params[0].Set("camera", true); -    camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; -    ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; -    nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};      nfc_params[1] = right_joycon;      nfc_params[1].Set("nfc", true); +    // Only map virtual devices to the first controller +    if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { +        camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; +        ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"}; +        nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; +    } +      output_params[LeftIndex] = left_joycon;      output_params[RightIndex] = right_joycon;      output_params[2] = camera_params[1]; @@ -1176,10 +1180,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {          return;      } -    controller.nfc_state = { -        controller.nfc_values.state, -        controller.nfc_values.data, -    }; +    controller.nfc_state = controller.nfc_values;  }  bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { @@ -1308,6 +1309,73 @@ bool EmulatedController::HasNfc() const {      return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);  } +bool EmulatedController::AddNfcHandle() { +    nfc_handles++; +    return SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::NFC) == +           Common::Input::DriverResult::Success; +} + +bool EmulatedController::RemoveNfcHandle() { +    nfc_handles--; +    if (nfc_handles <= 0) { +        return SetPollingMode(EmulatedDeviceIndex::RightIndex, +                              Common::Input::PollingMode::Active) == +               Common::Input::DriverResult::Success; +    } +    return true; +} + +bool EmulatedController::StartNfcPolling() { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    return nfc_output_device->StartNfcPolling() == Common::Input::NfcState::Success || +           nfc_virtual_output_device->StartNfcPolling() == Common::Input::NfcState::Success; +} + +bool EmulatedController::StopNfcPolling() { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    return nfc_output_device->StopNfcPolling() == Common::Input::NfcState::Success || +           nfc_virtual_output_device->StopNfcPolling() == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->ReadAmiiboData(data) == Common::Input::NfcState::Success; +} + +bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, +                                        Common::Input::MifareRequest& out_data) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->ReadMifareData(request, out_data) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->ReadMifareData(request, out_data) == +           Common::Input::NfcState::Success; +} + +bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->WriteMifareData(request) == Common::Input::NfcState::Success) { +        return true; +    } + +    return nfc_virtual_output_device->WriteMifareData(request) == Common::Input::NfcState::Success; +} +  bool EmulatedController::WriteNfc(const std::vector<u8>& data) {      auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];      auto& nfc_virtual_output_device = output_devices[3]; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 09fe1a0ab..d511e5fac 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -97,10 +97,7 @@ struct RingSensorForce {      f32 force;  }; -struct NfcState { -    Common::Input::NfcState state{}; -    std::vector<u8> data{}; -}; +using NfcState = Common::Input::NfcStatus;  struct ControllerMotion {      Common::Vec3f accel{}; @@ -393,9 +390,31 @@ public:      /// Returns true if the device has nfc support      bool HasNfc() const; +    /// Sets the joycon in nfc mode and increments the handle count +    bool AddNfcHandle(); + +    /// Decrements the handle count if zero sets the joycon in active mode +    bool RemoveNfcHandle(); + +    /// Start searching for nfc tags +    bool StartNfcPolling(); + +    /// Stop searching for nfc tags +    bool StopNfcPolling(); + +    /// Returns true if the nfc tag was readable +    bool ReadAmiiboData(std::vector<u8>& data); +      /// Returns true if the nfc tag was written      bool WriteNfc(const std::vector<u8>& data); +    /// Returns true if the nfc tag was readable +    bool ReadMifareData(const Common::Input::MifareRequest& request, +                        Common::Input::MifareRequest& out_data); + +    /// Returns true if the nfc tag was written +    bool WriteMifareData(const Common::Input::MifareRequest& request); +      /// Returns the led pattern corresponding to this emulated controller      LedPattern GetLedPattern() const; @@ -532,6 +551,7 @@ private:      bool system_buttons_enabled{true};      f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};      u32 turbo_button_state{0}; +    std::size_t nfc_handles{0};      // Temporary values to avoid doing changes while the controller is in configuring mode      NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 4ccb1c596..a05716fd8 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -299,11 +299,7 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal      Common::Input::NfcStatus nfc{};      switch (callback.type) {      case Common::Input::InputType::Nfc: -        nfc = { -            .state = callback.nfc_status, -            .data = callback.raw_data, -        }; -        break; +        return callback.nfc_status;      default:          LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);          break; diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index b7da3eee7..3e5b735b1 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -3,6 +3,7 @@  #include "common/assert.h"  #include "common/common_types.h" +#include "common/scratch_buffer.h"  #include "core/hle/kernel/k_scheduler.h"  #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"  #include "core/hle/kernel/k_synchronization_object.h" @@ -75,7 +76,7 @@ Result KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,                                      KSynchronizationObject** objects, const s32 num_objects,                                      s64 timeout) {      // Allocate space on stack for thread nodes. -    std::vector<ThreadListNode> thread_nodes(num_objects); +    std::array<ThreadListNode, Svc::ArgumentHandleCountMax> thread_nodes;      // Prepare for wait.      KThread* thread = GetCurrentThreadPointer(kernel); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 908811e2c..adb6ec581 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -909,7 +909,7 @@ Result KThread::SetActivity(Svc::ThreadActivity activity) {      R_SUCCEED();  } -Result KThread::GetThreadContext3(std::vector<u8>& out) { +Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {      // Lock ourselves.      KScopedLightLock lk{m_activity_pause_lock}; @@ -927,15 +927,13 @@ Result KThread::GetThreadContext3(std::vector<u8>& out) {                  // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.                  auto context = GetContext64();                  context.pstate &= 0xFF0FFE20; - -                out.resize(sizeof(context)); +                out.resize_destructive(sizeof(context));                  std::memcpy(out.data(), std::addressof(context), sizeof(context));              } else {                  // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.                  auto context = GetContext32();                  context.cpsr &= 0xFF0FFE20; - -                out.resize(sizeof(context)); +                out.resize_destructive(sizeof(context));                  std::memcpy(out.data(), std::addressof(context), sizeof(context));              }          } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 37fe5db77..dd662b3f8 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -15,6 +15,7 @@  #include "common/intrusive_list.h"  #include "common/intrusive_red_black_tree.h" +#include "common/scratch_buffer.h"  #include "common/spin_lock.h"  #include "core/arm/arm_interface.h"  #include "core/hle/kernel/k_affinity_mask.h" @@ -567,7 +568,7 @@ public:      void RemoveWaiter(KThread* thread); -    Result GetThreadContext3(std::vector<u8>& out); +    Result GetThreadContext3(Common::ScratchBuffer<u8>& out);      KThread* RemoveUserWaiterByKey(bool* out_has_waiters, KProcessAddress key) {          return this->RemoveWaiterByKey(out_has_waiters, key, false); diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index ea03068aa..60247df2e 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -2,6 +2,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "common/scope_exit.h" +#include "common/scratch_buffer.h"  #include "core/core.h"  #include "core/hle/kernel/k_client_session.h"  #include "core/hle/kernel/k_process.h" @@ -45,11 +46,11 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad                   handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)),               ResultInvalidPointer); -    std::vector<Handle> handles(num_handles); +    std::array<Handle, Svc::ArgumentHandleCountMax> handles;      GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles);      // Convert handle list to object table. -    std::vector<KSynchronizationObject*> objs(num_handles); +    std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;      R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(),                                                                       num_handles),               ResultInvalidHandle); @@ -80,7 +81,7 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad          // Wait for an object.          s32 index;          Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), -                                                     static_cast<s32>(objs.size()), timeout_ns); +                                                     num_handles, timeout_ns);          if (result == ResultTimedOut) {              R_RETURN(result);          } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 04d65f0bd..53df5bcd8 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -2,6 +2,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "common/scope_exit.h" +#include "common/scratch_buffer.h"  #include "core/core.h"  #include "core/hle/kernel/k_process.h"  #include "core/hle/kernel/k_readable_event.h" @@ -54,7 +55,7 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons      // Get the synchronization context.      auto& kernel = system.Kernel();      auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); -    std::vector<KSynchronizationObject*> objs(num_handles); +    std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> objs;      // Copy user handles.      if (num_handles > 0) { @@ -72,8 +73,8 @@ static Result WaitSynchronization(Core::System& system, int32_t* out_index, cons      });      // Wait on the objects. -    Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), -                                              static_cast<s32>(objs.size()), timeout_ns); +    Result res = +        KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout_ns);      R_SUCCEED_IF(res == ResultSessionClosed);      R_RETURN(res); @@ -87,8 +88,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha      // Ensure number of handles is valid.      R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); - -    std::vector<Handle> handles(num_handles); +    std::array<Handle, Svc::ArgumentHandleCountMax> handles;      if (num_handles > 0) {          GetCurrentMemory(system.Kernel())              .ReadBlock(user_handles, handles.data(), num_handles * sizeof(Handle)); diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 37b54079c..36b94e6bf 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -174,7 +174,7 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha          }          // Get the thread context. -        std::vector<u8> context; +        static thread_local Common::ScratchBuffer<u8> context;          R_TRY(thread->GetThreadContext3(context));          // Copy the thread context to user space. diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index 8b754e9d4..19ed184e8 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -141,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)      applet_output.device_handle = applet_input_common.device_handle;      applet_output.result = CabinetResult::Cancel;      const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); -    const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false); +    const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);      nfp_device->Finalize();      if (reg_result.IsSuccess()) { diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index f0640c64f..c8d574993 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -5,6 +5,7 @@  #include "audio_core/renderer/audio_device.h"  #include "common/common_funcs.h"  #include "common/logging/log.h" +#include "common/settings.h"  #include "common/string_util.h"  #include "core/core.h"  #include "core/hle/kernel/k_event.h" @@ -123,19 +124,13 @@ private:      void GetReleasedAudioInBuffer(HLERequestContext& ctx) {          const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); -        std::vector<u64> released_buffers(write_buffer_size); +        tmp_buffer.resize_destructive(write_buffer_size); +        tmp_buffer[0] = 0; -        const auto count = impl->GetReleasedBuffers(released_buffers); +        const auto count = impl->GetReleasedBuffers(tmp_buffer); -        [[maybe_unused]] std::string tags{}; -        for (u32 i = 0; i < count; i++) { -            tags += fmt::format("{:08X}, ", released_buffers[i]); -        } -        [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; -        LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, -                  tags); +        ctx.WriteBuffer(tmp_buffer); -        ctx.WriteBuffer(released_buffers);          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(ResultSuccess);          rb.Push(count); @@ -200,6 +195,7 @@ private:      KernelHelpers::ServiceContext service_context;      Kernel::KEvent* event;      std::shared_ptr<AudioCore::AudioIn::In> impl; +    Common::ScratchBuffer<u64> tmp_buffer;  };  AudInU::AudInU(Core::System& system_) diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 3e62fa4fc..032c8c11f 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -123,19 +123,13 @@ private:      void GetReleasedAudioOutBuffers(HLERequestContext& ctx) {          const auto write_buffer_size = ctx.GetWriteBufferNumElements<u64>(); -        std::vector<u64> released_buffers(write_buffer_size); +        tmp_buffer.resize_destructive(write_buffer_size); +        tmp_buffer[0] = 0; -        const auto count = impl->GetReleasedBuffers(released_buffers); +        const auto count = impl->GetReleasedBuffers(tmp_buffer); -        [[maybe_unused]] std::string tags{}; -        for (u32 i = 0; i < count; i++) { -            tags += fmt::format("{:08X}, ", released_buffers[i]); -        } -        [[maybe_unused]] const auto sessionid{impl->GetSystem().GetSessionId()}; -        LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, -                  tags); +        ctx.WriteBuffer(tmp_buffer); -        ctx.WriteBuffer(released_buffers);          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(ResultSuccess);          rb.Push(count); @@ -211,6 +205,7 @@ private:      KernelHelpers::ServiceContext service_context;      Kernel::KEvent* event;      std::shared_ptr<AudioCore::AudioOut::Out> impl; +    Common::ScratchBuffer<u64> tmp_buffer;  };  AudOutU::AudOutU(Core::System& system_) diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 7086d4750..12845c23a 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -116,28 +116,26 @@ private:          // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for          // checking size 0. Performance size is 0 for most games. -        std::vector<u8> output{}; -        std::vector<u8> performance{};          auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};          if (is_buffer_b) {              const auto buffersB{ctx.BufferDescriptorB()}; -            output.resize(buffersB[0].Size(), 0); -            performance.resize(buffersB[1].Size(), 0); +            tmp_output.resize_destructive(buffersB[0].Size()); +            tmp_performance.resize_destructive(buffersB[1].Size());          } else {              const auto buffersC{ctx.BufferDescriptorC()}; -            output.resize(buffersC[0].Size(), 0); -            performance.resize(buffersC[1].Size(), 0); +            tmp_output.resize_destructive(buffersC[0].Size()); +            tmp_performance.resize_destructive(buffersC[1].Size());          } -        auto result = impl->RequestUpdate(input, performance, output); +        auto result = impl->RequestUpdate(input, tmp_performance, tmp_output);          if (result.IsSuccess()) {              if (is_buffer_b) { -                ctx.WriteBufferB(output.data(), output.size(), 0); -                ctx.WriteBufferB(performance.data(), performance.size(), 1); +                ctx.WriteBufferB(tmp_output.data(), tmp_output.size(), 0); +                ctx.WriteBufferB(tmp_performance.data(), tmp_performance.size(), 1);              } else { -                ctx.WriteBufferC(output.data(), output.size(), 0); -                ctx.WriteBufferC(performance.data(), performance.size(), 1); +                ctx.WriteBufferC(tmp_output.data(), tmp_output.size(), 0); +                ctx.WriteBufferC(tmp_performance.data(), tmp_performance.size(), 1);              }          } else {              LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); @@ -235,6 +233,8 @@ private:      Kernel::KEvent* rendered_event;      Manager& manager;      std::unique_ptr<Renderer> impl; +    Common::ScratchBuffer<u8> tmp_output; +    Common::ScratchBuffer<u8> tmp_performance;  };  class IAudioDevice final : public ServiceFramework<IAudioDevice> { diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 24ce37e87..d8e9c8719 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,6 +4,7 @@  #pragma once  #include "audio_core/audio_render_manager.h" +#include "common/scratch_buffer.h"  #include "core/hle/service/kernel_helpers.h"  #include "core/hle/service/service.h" diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 451ac224a..c835f6cb7 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -68,13 +68,13 @@ private:                                   ExtraBehavior extra_behavior) {          u32 consumed = 0;          u32 sample_count = 0; -        std::vector<opus_int16> samples(ctx.GetWriteBufferNumElements<opus_int16>()); +        tmp_samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>());          if (extra_behavior == ExtraBehavior::ResetContext) {              ResetDecoderContext();          } -        if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { +        if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), tmp_samples, performance)) {              LOG_ERROR(Audio, "Failed to decode opus data");              IPC::ResponseBuilder rb{ctx, 2};              // TODO(ogniK): Use correct error code @@ -90,11 +90,11 @@ private:          if (performance) {              rb.Push<u64>(*performance);          } -        ctx.WriteBuffer(samples); +        ctx.WriteBuffer(tmp_samples);      }      bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, -                        std::vector<opus_int16>& output, u64* out_performance_time) const { +                        std::span<opus_int16> output, u64* out_performance_time) const {          const auto start_time = std::chrono::steady_clock::now();          const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);          if (sizeof(OpusPacketHeader) > input.size()) { @@ -154,6 +154,7 @@ private:      OpusDecoderPtr decoder;      u32 sample_rate;      u32 channel_count; +    Common::ScratchBuffer<opus_int16> tmp_samples;  };  class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index f4b180b06..5bf289818 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -93,7 +93,8 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {      const auto nfc_status = npad_device->GetNfc();      switch (nfc_status.state) {      case Common::Input::NfcState::NewAmiibo: -        LoadNfcTag(nfc_status.data); +        LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length, +                   nfc_status.uuid);          break;      case Common::Input::NfcState::AmiiboRemoved:          if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { @@ -108,28 +109,46 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {      }  } -bool NfcDevice::LoadNfcTag(std::span<const u8> data) { +bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {      if (device_state != DeviceState::SearchingForTag) {          LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);          return false;      } +    if ((protocol & static_cast<u8>(allowed_protocols)) == 0) { +        LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol); +        return false; +    } + +    real_tag_info = { +        .uuid = uuid, +        .uuid_length = uuid_length, +        .protocol = static_cast<NfcProtocol>(protocol), +        .tag_type = static_cast<TagType>(tag_type), +    }; + +    device_state = DeviceState::TagFound; +    deactivate_event->GetReadableEvent().Clear(); +    activate_event->Signal(); +    return true; +} + +bool NfcDevice::LoadAmiiboData() { +    std::vector<u8> data{}; + +    if (!npad_device->ReadAmiiboData(data)) { +        return false; +    } +      if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {          LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());          return false;      } -    mifare_data.resize(data.size()); -    memcpy(mifare_data.data(), data.data(), data.size()); -      memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));      is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);      is_write_protected = false; -    device_state = DeviceState::TagFound; -    deactivate_event->GetReadableEvent().Clear(); -    activate_event->Signal(); -      // Fallback for plain amiibos      if (is_plain_amiibo) {          LOG_INFO(Service_NFP, "Using plain amiibo"); @@ -147,6 +166,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {          return true;      } +    LOG_INFO(Service_NFP, "Using encrypted amiibo");      tag_data = {};      memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));      return true; @@ -162,7 +182,6 @@ void NfcDevice::CloseNfcTag() {      device_state = DeviceState::TagRemoved;      encrypted_tag_data = {};      tag_data = {}; -    mifare_data = {};      activate_event->GetReadableEvent().Clear();      deactivate_event->Signal();  } @@ -179,8 +198,12 @@ void NfcDevice::Initialize() {      device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;      encrypted_tag_data = {};      tag_data = {}; -    mifare_data = {}; -    is_initalized = true; + +    if (device_state != DeviceState::Initialized) { +        return; +    } + +    is_initalized = npad_device->AddNfcHandle();  }  void NfcDevice::Finalize() { @@ -190,6 +213,11 @@ void NfcDevice::Finalize() {      if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {          StopDetection();      } + +    if (device_state != DeviceState::Unavailable) { +        npad_device->RemoveNfcHandle(); +    } +      device_state = DeviceState::Unavailable;      is_initalized = false;  } @@ -200,10 +228,8 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {          return ResultWrongDeviceState;      } -    if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, -                                    Common::Input::PollingMode::NFC) != -        Common::Input::DriverResult::Success) { -        LOG_ERROR(Service_NFC, "Nfc not supported"); +    if (!npad_device->StartNfcPolling()) { +        LOG_ERROR(Service_NFC, "Nfc polling not supported");          return ResultNfcDisabled;      } @@ -213,9 +239,6 @@ Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {  }  Result NfcDevice::StopDetection() { -    npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, -                                Common::Input::PollingMode::Active); -      if (device_state == DeviceState::Initialized) {          return ResultSuccess;      } @@ -225,6 +248,7 @@ Result NfcDevice::StopDetection() {      }      if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { +        npad_device->StopNfcPolling();          device_state = DeviceState::Initialized;          return ResultSuccess;      } @@ -233,7 +257,7 @@ Result NfcDevice::StopDetection() {      return ResultWrongDeviceState;  } -Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {      if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {          LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);          if (device_state == DeviceState::TagRemoved) { @@ -242,41 +266,15 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {          return ResultWrongDeviceState;      } -    UniqueSerialNumber uuid{}; -    u8 uuid_length{}; -    NfcProtocol protocol{NfcProtocol::TypeA}; -    TagType tag_type{TagType::Type2}; - -    if (is_mifare) { -        tag_type = TagType::Mifare; -        uuid_length = sizeof(NFP::NtagTagUuid); -        memcpy(uuid.data(), mifare_data.data(), uuid_length); -    } else { -        tag_type = TagType::Type2; -        uuid_length = sizeof(NFP::NtagTagUuid); -        NFP::NtagTagUuid nUuid{ -            .part1 = encrypted_tag_data.uuid.part1, -            .part2 = encrypted_tag_data.uuid.part2, -            .nintendo_id = encrypted_tag_data.uuid.nintendo_id, -        }; -        memcpy(uuid.data(), &nUuid, uuid_length); +    tag_info = real_tag_info; -        // Generate random UUID to bypass amiibo load limits -        if (Settings::values.random_amiibo_id) { -            Common::TinyMT rng{}; -            rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); -            rng.GenerateRandomBytes(uuid.data(), uuid_length); -        } +    // Generate random UUID to bypass amiibo load limits +    if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) { +        Common::TinyMT rng{}; +        rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); +        rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);      } -    // Protocol and tag type may change here -    tag_info = { -        .uuid = uuid, -        .uuid_length = uuid_length, -        .protocol = protocol, -        .tag_type = tag_type, -    }; -      return ResultSuccess;  } @@ -293,7 +291,7 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter      Result result = ResultSuccess;      TagInfo tag_info{}; -    result = GetTagInfo(tag_info, true); +    result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -307,6 +305,8 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter          return ResultInvalidArgument;      } +    Common::Input::MifareRequest request{}; +    Common::Input::MifareRequest out_data{};      const auto unknown = parameters[0].sector_key.unknown;      for (std::size_t i = 0; i < parameters.size(); i++) {          if (unknown != parameters[i].sector_key.unknown) { @@ -315,25 +315,29 @@ Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameter      }      for (std::size_t i = 0; i < parameters.size(); i++) { -        result = ReadMifare(parameters[i], read_block_data[i]); -        if (result.IsError()) { -            break; +        if (parameters[i].sector_key.command == MifareCmd::None) { +            continue;          } +        request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); +        request.data[i].sector = parameters[i].sector_number; +        memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), +               sizeof(KeyData));      } -    return result; -} - -Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, -                             MifareReadBlockData& read_block_data) const { -    const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); -    read_block_data.sector_number = parameter.sector_number; -    if (mifare_data.size() < sector_index + sizeof(DataBlock)) { +    if (!npad_device->ReadMifareData(request, out_data)) {          return ResultMifareError288;      } -    // TODO: Use parameter.sector_key to read encrypted data -    memcpy(read_block_data.data.data(), mifare_data.data() + sector_index, sizeof(DataBlock)); +    for (std::size_t i = 0; i < read_block_data.size(); i++) { +        if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) { +            continue; +        } + +        read_block_data[i] = { +            .data = out_data.data[i].data, +            .sector_number = out_data.data[i].sector, +        }; +    }      return ResultSuccess;  } @@ -342,7 +346,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet      Result result = ResultSuccess;      TagInfo tag_info{}; -    result = GetTagInfo(tag_info, true); +    result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -363,42 +367,25 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet          }      } +    Common::Input::MifareRequest request{};      for (std::size_t i = 0; i < parameters.size(); i++) { -        result = WriteMifare(parameters[i]); -        if (result.IsError()) { -            break; +        if (parameters[i].sector_key.command == MifareCmd::None) { +            continue;          } +        request.data[i].command = static_cast<u8>(parameters[i].sector_key.command); +        request.data[i].sector = parameters[i].sector_number; +        memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(), +               sizeof(KeyData)); +        memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));      } -    if (!npad_device->WriteNfc(mifare_data)) { -        LOG_ERROR(Service_NFP, "Error writing to file"); +    if (!npad_device->WriteMifareData(request)) {          return ResultMifareError288;      }      return result;  } -Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) { -    const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); - -    if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ResultTagRemoved; -        } -        return ResultWrongDeviceState; -    } - -    if (mifare_data.size() < sector_index + sizeof(DataBlock)) { -        return ResultMifareError288; -    } - -    // TODO: Use parameter.sector_key to encrypt the data -    memcpy(mifare_data.data() + sector_index, parameter.data.data(), sizeof(DataBlock)); - -    return ResultSuccess; -} -  Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,                                             std::span<const u8> command_data,                                             std::span<u8> out_data) { @@ -412,6 +399,11 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target          return ResultWrongDeviceState;      } +    if (!LoadAmiiboData()) { +        LOG_ERROR(Service_NFP, "Not an amiibo"); +        return ResultInvalidTagType; +    } +      if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {          LOG_ERROR(Service_NFP, "Not an amiibo");          return ResultInvalidTagType; @@ -562,7 +554,7 @@ Result NfcDevice::Restore() {      NFC::TagInfo tag_info{};      std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{}; -    Result result = GetTagInfo(tag_info, false); +    Result result = GetTagInfo(tag_info);      if (result.IsError()) {          return result; @@ -635,7 +627,7 @@ Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {      // TODO: Validate this data      common_info = {          .last_write_date = settings.write_date.GetWriteDate(), -        .write_counter = tag_data.write_counter, +        .write_counter = tag_data.application_write_counter,          .version = tag_data.amiibo_version,          .application_area_size = sizeof(NFP::ApplicationArea),      }; diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 7560210d6..0ed1ff34c 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -42,15 +42,12 @@ public:      Result StartDetection(NfcProtocol allowed_protocol);      Result StopDetection(); -    Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const; +    Result GetTagInfo(TagInfo& tag_info) const;      Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,                        std::span<MifareReadBlockData> read_block_data) const; -    Result ReadMifare(const MifareReadBlockParameter& parameter, -                      MifareReadBlockData& read_block_data) const;      Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); -    Result WriteMifare(const MifareWriteBlockParameter& parameter);      Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,                                      std::span<const u8> command_data, std::span<u8> out_data); @@ -105,7 +102,8 @@ public:  private:      void NpadUpdate(Core::HID::ControllerTriggerType type); -    bool LoadNfcTag(std::span<const u8> data); +    bool LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid); +    bool LoadAmiiboData();      void CloseNfcTag();      NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; @@ -140,8 +138,8 @@ private:      bool is_write_protected{};      NFP::MountTarget mount_target{NFP::MountTarget::None}; +    TagInfo real_tag_info{};      NFP::NTAG215File tag_data{}; -    std::vector<u8> mifare_data{};      NFP::EncryptedNTAG215File encrypted_tag_data{};  }; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index b0456508e..562f3a28e 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -29,6 +29,9 @@ DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContex  }  DeviceManager ::~DeviceManager() { +    if (is_initialized) { +        Finalize(); +    }      service_context.CloseEvent(availability_change_event);  } @@ -125,14 +128,14 @@ Result DeviceManager::StopDetection(u64 device_handle) {      return result;  } -Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const { +Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const {      std::scoped_lock lock{mutex};      std::shared_ptr<NfcDevice> device = nullptr;      auto result = GetDeviceHandle(device_handle, device);      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, is_mifare); +        result = device->GetTagInfo(tag_info);          result = VerifyDeviceResult(device, result);      } @@ -546,7 +549,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons      NFC::TagInfo tag_info{};      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, false); +        result = device->GetTagInfo(tag_info);      }      if (result.IsSuccess()) { @@ -565,7 +568,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat      NFC::TagInfo tag_info{};      if (result.IsSuccess()) { -        result = device->GetTagInfo(tag_info, false); +        result = device->GetTagInfo(tag_info);      }      if (result.IsSuccess()) { diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index 2971e280f..c61ba0cf3 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -33,7 +33,7 @@ public:      Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;      Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);      Result StopDetection(u64 device_handle); -    Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const; +    Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const;      Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;      Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;      Result ReadMifare(u64 device_handle, diff --git a/src/core/hle/service/nfc/mifare_types.h b/src/core/hle/service/nfc/mifare_types.h index 75b59f021..467937399 100644 --- a/src/core/hle/service/nfc/mifare_types.h +++ b/src/core/hle/service/nfc/mifare_types.h @@ -11,9 +11,10 @@  namespace Service::NFC {  enum class MifareCmd : u8 { +    None = 0x00, +    Read = 0x30,      AuthA = 0x60,      AuthB = 0x61, -    Read = 0x30,      Write = 0xA0,      Transfer = 0xB0,      Decrement = 0xC0, @@ -35,17 +36,17 @@ static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");  // This is nn::nfc::MifareReadBlockParameter  struct MifareReadBlockParameter { -    u8 sector_number; +    u8 sector_number{};      INSERT_PADDING_BYTES(0x7); -    SectorKey sector_key; +    SectorKey sector_key{};  };  static_assert(sizeof(MifareReadBlockParameter) == 0x18,                "MifareReadBlockParameter is an invalid size");  // This is nn::nfc::MifareReadBlockData  struct MifareReadBlockData { -    DataBlock data; -    u8 sector_number; +    DataBlock data{}; +    u8 sector_number{};      INSERT_PADDING_BYTES(0x7);  };  static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 130fb7f78..e7ca7582e 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -174,8 +174,7 @@ void NfcInterface::GetTagInfo(HLERequestContext& ctx) {      LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);      TagInfo tag_info{}; -    auto result = -        GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare); +    auto result = GetManager()->GetTagInfo(device_handle, tag_info);      result = TranslateResultToServiceError(result);      if (result.IsSuccess()) { @@ -216,8 +215,8 @@ void NfcInterface::ReadMifare(HLERequestContext& ctx) {      memcpy(read_commands.data(), buffer.data(),             number_of_commands * sizeof(MifareReadBlockParameter)); -    LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", -             device_handle, number_of_commands); +    LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle, +             number_of_commands);      std::vector<MifareReadBlockData> out_data(number_of_commands);      auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data); diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index ab1f30f9e..a04538d5d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -34,7 +34,7 @@ public:       * @returns The result code of the ioctl.       */      virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output) = 0; +                            std::span<u8> output) = 0;      /**       * Handles an ioctl2 request. @@ -45,7 +45,7 @@ public:       * @returns The result code of the ioctl.       */      virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::span<const u8> inline_input, std::vector<u8>& output) = 0; +                            std::span<const u8> inline_input, std::span<u8> output) = 0;      /**       * Handles an ioctl3 request. @@ -56,7 +56,7 @@ public:       * @returns The result code of the ioctl.       */      virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output, std::vector<u8>& inline_output) = 0; +                            std::span<u8> output, std::span<u8> inline_output) = 0;      /**       * Called once a device is opened diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 0fe242e9d..05a43d8dc 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -18,19 +18,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)  nvdisp_disp0::~nvdisp_disp0() = default;  NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output) { +                              std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::span<const u8> inline_input, std::vector<u8>& output) { +                              std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output, std::vector<u8>& inline_output) { +                              std::span<u8> output, std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index bcd0e3ed5..daee05fe8 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -26,11 +26,11 @@ public:      ~nvdisp_disp0() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 681bd0867..07e570a9f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -28,7 +28,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con  nvhost_as_gpu::~nvhost_as_gpu() = default;  NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                               std::vector<u8>& output) { +                               std::span<u8> output) {      switch (command.group) {      case 'A':          switch (command.cmd) { @@ -61,13 +61,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i  }  NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                               std::span<const u8> inline_input, std::vector<u8>& output) { +                               std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                               std::vector<u8>& output, std::vector<u8>& inline_output) { +                               std::span<u8> output, std::span<u8> inline_output) {      switch (command.group) {      case 'A':          switch (command.cmd) { @@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i  void nvhost_as_gpu::OnOpen(DeviceFD fd) {}  void nvhost_as_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::span<u8> output) {      IoctlAllocAsEx params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& ou      return NvResult::Success;  } -NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::span<u8> output) {      IoctlAllocSpace params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {      mapping_map.erase(offset);  } -NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::span<u8> output) {      IoctlFreeSpace params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -266,15 +266,14 @@ NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& ou      return NvResult::Success;  } -NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::span<u8> output) {      const auto num_entries = input.size() / sizeof(IoctlRemapEntry);      LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); -    std::vector<IoctlRemapEntry> entries(num_entries); -    std::memcpy(entries.data(), input.data(), input.size()); -      std::scoped_lock lock(mutex); +    entries.resize_destructive(num_entries); +    std::memcpy(entries.data(), input.data(), input.size());      if (!vm.initialised) {          return NvResult::BadValue; @@ -320,7 +319,7 @@ NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output      return NvResult::Success;  } -NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::span<u8> output) {      IoctlMapBufferEx params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -424,7 +423,7 @@ NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>&      return NvResult::Success;  } -NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {      IoctlUnmapBuffer params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -463,7 +462,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>&      return NvResult::Success;  } -NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::span<u8> output) {      IoctlBindChannel params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); @@ -492,7 +491,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {      };  } -NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output) {      IoctlGetVaRegions params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -511,8 +510,8 @@ NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>&      return NvResult::Success;  } -NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output, -                                     std::vector<u8>& inline_output) { +NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::span<u8> output, +                                     std::span<u8> inline_output) {      IoctlGetVaRegions params{};      std::memcpy(¶ms, input.data(), input.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 1aba8d579..2af3e1260 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -15,6 +15,7 @@  #include "common/address_space.h"  #include "common/common_funcs.h"  #include "common/common_types.h" +#include "common/scratch_buffer.h"  #include "common/swap.h"  #include "core/hle/service/nvdrv/core/nvmap.h"  #include "core/hle/service/nvdrv/devices/nvdevice.h" @@ -48,11 +49,11 @@ public:      ~nvhost_as_gpu() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -138,18 +139,18 @@ private:      static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,                    "IoctlGetVaRegions is incorrect size"); -    NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output); -    NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output); -    NvResult Remap(std::span<const u8> input, std::vector<u8>& output); -    NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output); -    NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); -    NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output); -    NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output); +    NvResult AllocAsEx(std::span<const u8> input, std::span<u8> output); +    NvResult AllocateSpace(std::span<const u8> input, std::span<u8> output); +    NvResult Remap(std::span<const u8> input, std::span<u8> output); +    NvResult MapBufferEx(std::span<const u8> input, std::span<u8> output); +    NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); +    NvResult FreeSpace(std::span<const u8> input, std::span<u8> output); +    NvResult BindChannel(std::span<const u8> input, std::span<u8> output);      void GetVARegionsImpl(IoctlGetVaRegions& params); -    NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output, -                          std::vector<u8>& inline_output); +    NvResult GetVARegions(std::span<const u8> input, std::span<u8> output); +    NvResult GetVARegions(std::span<const u8> input, std::span<u8> output, +                          std::span<u8> inline_output);      void FreeMappingLocked(u64 offset); @@ -212,6 +213,7 @@ private:          bool initialised{};      } vm;      std::shared_ptr<Tegra::MemoryManager> gmmu; +    Common::ScratchBuffer<IoctlRemapEntry> entries;      // s32 channel{};      // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index e12025560..4d55554b4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -35,7 +35,7 @@ nvhost_ctrl::~nvhost_ctrl() {  }  NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                             std::vector<u8>& output) { +                             std::span<u8> output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -64,13 +64,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inp  }  NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                             std::span<const u8> inline_input, std::vector<u8>& output) { +                             std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                             std::vector<u8>& output, std::vector<u8>& inline_outpu) { +                             std::span<u8> output, std::span<u8> inline_outpu) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } @@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}  void nvhost_ctrl::OnClose(DeviceFD fd) {} -NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output) {      IocGetConfigParams params{};      std::memcpy(¶ms, input.data(), sizeof(params));      LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), @@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8      return NvResult::ConfigVarNotFound; // Returns error on production mode  } -NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, +NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::span<u8> output,                                         bool is_allocation) {      IocCtrlEventWaitParams params{};      std::memcpy(¶ms, input.data(), sizeof(params)); @@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {      return NvResult::Success;  } -NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output) {      IocCtrlEventRegisterParams params{};      std::memcpy(¶ms, input.data(), sizeof(params));      const u32 event_id = params.user_event_id; @@ -252,7 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vecto      return NvResult::Success;  } -NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output) {      IocCtrlEventUnregisterParams params{};      std::memcpy(¶ms, input.data(), sizeof(params));      const u32 event_id = params.user_event_id & 0x00FF; @@ -262,8 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vec      return FreeEvent(event_id);  } -NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, -                                                  std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output) {      IocCtrlEventUnregisterBatchParams params{};      std::memcpy(¶ms, input.data(), sizeof(params));      u64 event_mask = params.user_events; @@ -281,7 +280,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,      return NvResult::Success;  } -NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output) {      IocCtrlEventClearParams params{};      std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index dd2e7888a..2efed4862 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -26,11 +26,11 @@ public:      ~nvhost_ctrl() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -186,13 +186,12 @@ private:      static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,                    "IocCtrlEventKill is incorrect size"); -    NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output, -                              bool is_allocation); -    NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output); +    NvResult NvOsGetConfigU32(std::span<const u8> input, std::span<u8> output); +    NvResult IocCtrlEventWait(std::span<const u8> input, std::span<u8> output, bool is_allocation); +    NvResult IocCtrlEventRegister(std::span<const u8> input, std::span<u8> output); +    NvResult IocCtrlEventUnregister(std::span<const u8> input, std::span<u8> output); +    NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::span<u8> output); +    NvResult IocCtrlClearEventWait(std::span<const u8> input, std::span<u8> output);      NvResult FreeEvent(u32 slot); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index be3c083db..6081d92e9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -22,7 +22,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {  }  NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                                 std::vector<u8>& output) { +                                 std::span<u8> output) {      switch (command.group) {      case 'G':          switch (command.cmd) { @@ -54,13 +54,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>  }  NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                                 std::span<const u8> inline_input, std::vector<u8>& output) { +                                 std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                                 std::vector<u8>& output, std::vector<u8>& inline_output) { +                                 std::span<u8> output, std::span<u8> inline_output) {      switch (command.group) {      case 'G':          switch (command.cmd) { @@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>  void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}  void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlCharacteristics params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -127,8 +127,8 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, -                                             std::vector<u8>& inline_output) { +NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::span<u8> output, +                                             std::span<u8> inline_output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlCharacteristics params{};      std::memcpy(¶ms, input.data(), input.size()); @@ -175,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vec      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output) {      IoctlGpuGetTpcMasksArgs params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); @@ -186,8 +186,8 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, -                                      std::vector<u8>& inline_output) { +NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::span<u8> output, +                                      std::span<u8> inline_output) {      IoctlGpuGetTpcMasksArgs params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); @@ -199,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlActiveSlotMask params{}; @@ -212,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vect      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlZcullGetCtxSize params{}; @@ -224,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlNvgpuGpuZcullGetInfoArgs params{}; @@ -247,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::span<u8> output) {      LOG_WARNING(Service_NVDRV, "(STUBBED) called");      IoctlZbcSetTable params{}; @@ -263,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::span<u8> output) {      LOG_WARNING(Service_NVDRV, "(STUBBED) called");      IoctlZbcQueryTable params{}; @@ -273,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::span<u8> output) {      LOG_WARNING(Service_NVDRV, "(STUBBED) called");      IoctlFlushL2 params{}; @@ -283,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& ou      return NvResult::Success;  } -NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlGetGpuTime params{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index b9333d9d3..97995551c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -22,11 +22,11 @@ public:      ~nvhost_ctrl_gpu() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -151,21 +151,21 @@ private:      };      static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); -    NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output, -                                std::vector<u8>& inline_output); - -    NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output, -                         std::vector<u8>& inline_output); - -    NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output); -    NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output); -    NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output); -    NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output); -    NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output); -    NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output); +    NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output); +    NvResult GetCharacteristics(std::span<const u8> input, std::span<u8> output, +                                std::span<u8> inline_output); + +    NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output); +    NvResult GetTPCMasks(std::span<const u8> input, std::span<u8> output, +                         std::span<u8> inline_output); + +    NvResult GetActiveSlotMask(std::span<const u8> input, std::span<u8> output); +    NvResult ZCullGetCtxSize(std::span<const u8> input, std::span<u8> output); +    NvResult ZCullGetInfo(std::span<const u8> input, std::span<u8> output); +    NvResult ZBCSetTable(std::span<const u8> input, std::span<u8> output); +    NvResult ZBCQueryTable(std::span<const u8> input, std::span<u8> output); +    NvResult FlushL2(std::span<const u8> input, std::span<u8> output); +    NvResult GetGpuTime(std::span<const u8> input, std::span<u8> output);      EventInterface& events_interface; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 453a965dc..46a25fcab 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -47,7 +47,7 @@ nvhost_gpu::~nvhost_gpu() {  }  NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output) { +                            std::span<u8> output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -99,7 +99,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu  };  NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::span<const u8> inline_input, std::vector<u8>& output) { +                            std::span<const u8> inline_input, std::span<u8> output) {      switch (command.group) {      case 'H':          switch (command.cmd) { @@ -113,7 +113,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> inpu  }  NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output, std::vector<u8>& inline_output) { +                            std::span<u8> output, std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } @@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu  void nvhost_gpu::OnOpen(DeviceFD fd) {}  void nvhost_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {      IoctlSetNvmapFD params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); @@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& outp      return NvResult::Success;  } -NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlClientData params{}; @@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& o      return NvResult::Success;  } -NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::span<u8> output) {      LOG_DEBUG(Service_NVDRV, "called");      IoctlClientData params{}; @@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& o      return NvResult::Success;  } -NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::span<u8> output) {      std::memcpy(&zcull_params, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,                zcull_params.mode); @@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& outpu      return NvResult::Success;  } -NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::span<u8> output) {      IoctlSetErrorNotifier params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, @@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>      return NvResult::Success;  } -NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::span<u8> output) {      std::memcpy(&channel_priority, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);      return NvResult::Success;  } -NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output) {      IoctlAllocGpfifoEx2 params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_WARNING(Service_NVDRV, @@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>&      return NvResult::Success;  } -NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::span<u8> output) {      IoctlAllocObjCtx params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, @@ -208,7 +208,8 @@ NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vecto      return NvResult::Success;  } -static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList( +    NvFence fence) {      return {          Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,                                    Tegra::SubmissionMode::Increasing), @@ -219,35 +220,35 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {      };  } -static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { -    std::vector<Tegra::CommandHeader> result{ +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList( +    NvFence fence) { +    boost::container::small_vector<Tegra::CommandHeader, 512> result{          Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,                                    Tegra::SubmissionMode::Increasing),          {}};      for (u32 count = 0; count < 2; ++count) { -        result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, -                                                      Tegra::SubmissionMode::Increasing)); -        result.emplace_back( +        result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, +                                                   Tegra::SubmissionMode::Increasing)); +        result.push_back(              BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));      }      return result;  } -static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { -    std::vector<Tegra::CommandHeader> result{ +static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList( +    NvFence fence) { +    boost::container::small_vector<Tegra::CommandHeader, 512> result{          Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,                                    Tegra::SubmissionMode::Increasing),          {}}; -    const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; - -    result.insert(result.end(), increment.begin(), increment.end()); - +    auto increment_list{BuildIncrementCommandList(fence)}; +    result.insert(result.end(), increment_list.begin(), increment_list.end());      return result;  } -NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, +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,                params.num_entries, params.flags.raw); @@ -293,7 +294,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>      return NvResult::Success;  } -NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, +NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,                                        bool kickoff) {      if (input.size() < sizeof(IoctlSubmitGpfifo)) {          UNIMPLEMENTED(); @@ -315,7 +316,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>  }  NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, -                                      std::vector<u8>& output) { +                                      std::span<u8> output) {      if (input.size() < sizeof(IoctlSubmitGpfifo)) {          UNIMPLEMENTED();          return NvResult::InvalidSize; @@ -327,7 +328,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const      return SubmitGPFIFOImpl(params, output, std::move(entries));  } -NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::span<u8> output) {      IoctlGetWaitbase params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));      LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); @@ -337,7 +338,7 @@ NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& out      return NvResult::Success;  } -NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::span<u8> output) {      IoctlChannelSetTimeout params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout));      LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); @@ -345,7 +346,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8      return NvResult::Success;  } -NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output) {      IoctlSetTimeslice params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice));      LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 3ca58202d..529c20526 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -41,11 +41,11 @@ public:      ~nvhost_gpu() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -186,23 +186,23 @@ private:      u32_le channel_priority{};      u32_le channel_timeslice{}; -    NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); -    NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output); -    NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output); -    NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output); -    NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output); -    NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output); -    NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output); -    NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, +    NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output); +    NvResult SetClientData(std::span<const u8> input, std::span<u8> output); +    NvResult GetClientData(std::span<const u8> input, std::span<u8> output); +    NvResult ZCullBind(std::span<const u8> input, std::span<u8> output); +    NvResult SetErrorNotifier(std::span<const u8> input, std::span<u8> output); +    NvResult SetChannelPriority(std::span<const u8> input, std::span<u8> output); +    NvResult AllocGPFIFOEx2(std::span<const u8> input, std::span<u8> output); +    NvResult AllocateObjectContext(std::span<const u8> input, std::span<u8> output); +    NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::span<u8> output,                                Tegra::CommandList&& entries); -    NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output, +    NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<u8> output,                                bool kickoff = false);      NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline, -                              std::vector<u8>& output); -    NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); -    NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output); -    NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output); +                              std::span<u8> output); +    NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); +    NvResult ChannelSetTimeout(std::span<const u8> input, std::span<u8> output); +    NvResult ChannelSetTimeslice(std::span<const u8> input, std::span<u8> output);      EventInterface& events_interface;      NvCore::Container& core; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index dc45169ad..a174442a6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -16,7 +16,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)  nvhost_nvdec::~nvhost_nvdec() = default;  NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output) { +                              std::span<u8> output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -56,13 +56,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in  }  NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::span<const u8> inline_input, std::vector<u8>& output) { +                              std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output, std::vector<u8>& inline_output) { +                              std::span<u8> output, std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0d615bbcb..ad2233c49 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -14,11 +14,11 @@ public:      ~nvhost_nvdec() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 1ab51f10b..61649aa4a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -36,7 +36,7 @@ std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::si  // Writes the data in src to an offset into the dst vector. The offset is specified in bytes  // Returns the number of bytes written into dst.  template <typename T> -std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { +std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size_t offset) {      if (src.empty()) {          return 0;      } @@ -72,8 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {      return NvResult::Success;  } -NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, -                                     std::vector<u8>& output) { +NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output) {      IoctlSubmit params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit));      LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); @@ -121,7 +120,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,      return NvResult::Success;  } -NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::span<u8> output) {      IoctlGetSyncpoint params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint));      LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); @@ -133,7 +132,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vecto      return NvResult::Success;  } -NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::span<u8> output) {      IoctlGetWaitbase params{};      LOG_CRITICAL(Service_NVDRV, "called WAITBASE");      std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); @@ -142,7 +141,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector      return NvResult::Success;  } -NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::span<u8> output) {      IoctlMapBuffer params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));      std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -159,7 +158,7 @@ NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u      return NvResult::Success;  } -NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::span<u8> output) {      IoctlMapBuffer params{};      std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));      std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); @@ -173,7 +172,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector      return NvResult::Success;  } -NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::span<u8> output) {      std::memcpy(&submit_timeout, input.data(), input.size());      LOG_WARNING(Service_NVDRV, "(STUBBED) called");      return NvResult::Success; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 5af26a26f..9bb573bfe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -108,12 +108,12 @@ protected:      /// Ioctl command implementations      NvResult SetNVMAPfd(std::span<const u8> input); -    NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output); -    NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output); -    NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output); -    NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output); -    NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output); -    NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output); +    NvResult Submit(DeviceFD fd, std::span<const u8> input, std::span<u8> output); +    NvResult GetSyncpoint(std::span<const u8> input, std::span<u8> output); +    NvResult GetWaitbase(std::span<const u8> input, std::span<u8> output); +    NvResult MapBuffer(std::span<const u8> input, std::span<u8> output); +    NvResult UnmapBuffer(std::span<const u8> input, std::span<u8> output); +    NvResult SetSubmitTimeout(std::span<const u8> input, std::span<u8> output);      Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 39f30e7c8..a05c8cdae 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -13,7 +13,7 @@ nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}  nvhost_nvjpg::~nvhost_nvjpg() = default;  NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output) { +                              std::span<u8> output) {      switch (command.group) {      case 'H':          switch (command.cmd) { @@ -32,13 +32,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in  }  NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::span<const u8> inline_input, std::vector<u8>& output) { +                              std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                              std::vector<u8>& output, std::vector<u8>& inline_output) { +                              std::span<u8> output, std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } @@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in  void nvhost_nvjpg::OnOpen(DeviceFD fd) {}  void nvhost_nvjpg::OnClose(DeviceFD fd) {} -NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::span<u8> output) {      IoctlSetNvmapFD params{};      std::memcpy(¶ms, input.data(), input.size());      LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 41b57e872..5623e0d47 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -16,11 +16,11 @@ public:      ~nvhost_nvjpg() override;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -33,7 +33,7 @@ private:      s32_le nvmap_fd{}; -    NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output); +    NvResult SetNVMAPfd(std::span<const u8> input, std::span<u8> output);  };  } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index b0ea402a7..c0b8684c3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -16,7 +16,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)  nvhost_vic::~nvhost_vic() = default;  NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output) { +                            std::span<u8> output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -56,13 +56,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu  }  NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::span<const u8> inline_input, std::vector<u8>& output) { +                            std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                            std::vector<u8>& output, std::vector<u8>& inline_output) { +                            std::span<u8> output, std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b5e350a83..cadbcb0a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,11 +13,11 @@ public:      ~nvhost_vic();      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 07417f045..e7f7e273b 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -26,7 +26,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_)  nvmap::~nvmap() = default;  NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                       std::vector<u8>& output) { +                       std::span<u8> output) {      switch (command.group) {      case 0x1:          switch (command.cmd) { @@ -55,13 +55,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,  }  NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                       std::span<const u8> inline_input, std::vector<u8>& output) { +                       std::span<const u8> inline_input, std::span<u8> output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } -NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                       std::vector<u8>& output, std::vector<u8>& inline_output) { +NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                       std::span<u8> inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } @@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,  void nvmap::OnOpen(DeviceFD fd) {}  void nvmap::OnClose(DeviceFD fd) {} -NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocCreate(std::span<const u8> input, std::span<u8> output) {      IocCreateParams params;      std::memcpy(¶ms, input.data(), sizeof(params));      LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); @@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {      return NvResult::Success;  } -NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) {      IocAllocParams params;      std::memcpy(¶ms, input.data(), sizeof(params));      LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); @@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {      return result;  } -NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocGetId(std::span<const u8> input, std::span<u8> output) {      IocGetIdParams params;      std::memcpy(¶ms, input.data(), sizeof(params)); @@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {      return NvResult::Success;  } -NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocFromId(std::span<const u8> input, std::span<u8> output) {      IocFromIdParams params;      std::memcpy(¶ms, input.data(), sizeof(params)); @@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {      return NvResult::Success;  } -NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) { +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 };      IocParamParams params; @@ -241,7 +241,7 @@ NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {      return NvResult::Success;  } -NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) { +NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) {      IocFreeParams params;      std::memcpy(¶ms, input.data(), sizeof(params)); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 82bd3b118..40c65b430 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -27,11 +27,11 @@ public:      nvmap& operator=(const nvmap&) = delete;      NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::vector<u8>& output) override; +                    std::span<u8> output) override;      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output) override; -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output) override; +                    std::span<const u8> inline_input, std::span<u8> output) override; +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output) override;      void OnOpen(DeviceFD fd) override;      void OnClose(DeviceFD fd) override; @@ -106,12 +106,12 @@ private:      };      static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); -    NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocParam(std::span<const u8> input, std::vector<u8>& output); -    NvResult IocFree(std::span<const u8> input, std::vector<u8>& output); +    NvResult IocCreate(std::span<const u8> input, std::span<u8> output); +    NvResult IocAlloc(std::span<const u8> input, std::span<u8> output); +    NvResult IocGetId(std::span<const u8> input, std::span<u8> output); +    NvResult IocFromId(std::span<const u8> input, std::span<u8> output); +    NvResult IocParam(std::span<const u8> input, std::span<u8> output); +    NvResult IocFree(std::span<const u8> input, std::span<u8> output);      NvCore::Container& container;      NvCore::NvMap& file; diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 3d774eec4..9e46ee8dd 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -130,7 +130,7 @@ DeviceFD Module::Open(const std::string& device_name) {  }  NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, -                        std::vector<u8>& output) { +                        std::span<u8> output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; @@ -147,7 +147,7 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,  }  NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                        std::span<const u8> inline_input, std::vector<u8>& output) { +                        std::span<const u8> inline_input, std::span<u8> output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; @@ -163,8 +163,8 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,      return itr->second->Ioctl2(fd, command, input, inline_input, output);  } -NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, -                        std::vector<u8>& output, std::vector<u8>& inline_output) { +NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                        std::span<u8> inline_output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 668be742b..d8622b3ca 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -80,13 +80,13 @@ public:      DeviceFD Open(const std::string& device_name);      /// Sends an ioctl command to the specified file descriptor. -    NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output); +    NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output);      NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, -                    std::span<const u8> inline_input, std::vector<u8>& output); +                    std::span<const u8> inline_input, std::span<u8> output); -    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output, -                    std::vector<u8>& inline_output); +    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, +                    std::span<u8> inline_output);      /// Closes a device file descriptor and returns operation success.      NvResult Close(DeviceFD fd); diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index d010a1e03..348207e25 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -63,12 +63,12 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) {      }      // Check device -    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); +    tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));      const auto input_buffer = ctx.ReadBuffer(0); -    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); +    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output);      if (command.is_out != 0) { -        ctx.WriteBuffer(output_buffer); +        ctx.WriteBuffer(tmp_output);      }      IPC::ResponseBuilder rb{ctx, 3}; @@ -90,12 +90,12 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) {      const auto input_buffer = ctx.ReadBuffer(0);      const auto input_inlined_buffer = ctx.ReadBuffer(1); -    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); +    tmp_output.resize_destructive(ctx.GetWriteBufferSize(0));      const auto nv_result = -        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); +        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, tmp_output);      if (command.is_out != 0) { -        ctx.WriteBuffer(output_buffer); +        ctx.WriteBuffer(tmp_output);      }      IPC::ResponseBuilder rb{ctx, 3}; @@ -116,14 +116,12 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) {      }      const auto input_buffer = ctx.ReadBuffer(0); -    std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); -    std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); - -    const auto nv_result = -        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); +    tmp_output.resize_destructive(ctx.GetWriteBufferSize(0)); +    tmp_output_inline.resize_destructive(ctx.GetWriteBufferSize(1)); +    const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output_inline);      if (command.is_out != 0) { -        ctx.WriteBuffer(output_buffer, 0); -        ctx.WriteBuffer(output_buffer_inline, 1); +        ctx.WriteBuffer(tmp_output, 0); +        ctx.WriteBuffer(tmp_output_inline, 1);      }      IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 881ea1a6b..4b593ff90 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -4,6 +4,7 @@  #pragma once  #include <memory> +#include "common/scratch_buffer.h"  #include "core/hle/service/nvdrv/nvdrv.h"  #include "core/hle/service/service.h" @@ -33,6 +34,8 @@ private:      u64 pid{};      bool is_initialized{}; +    Common::ScratchBuffer<u8> tmp_output; +    Common::ScratchBuffer<u8> tmp_output_inline;  };  } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvnflinger/parcel.h b/src/core/hle/service/nvnflinger/parcel.h index fb56d75d7..23ba315a0 100644 --- a/src/core/hle/service/nvnflinger/parcel.h +++ b/src/core/hle/service/nvnflinger/parcel.h @@ -6,6 +6,7 @@  #include <memory>  #include <span>  #include <vector> +#include <boost/container/small_vector.hpp>  #include "common/alignment.h"  #include "common/assert.h" @@ -167,7 +168,7 @@ public:  private:      template <typename T>          requires(std::is_trivially_copyable_v<T>) -    void WriteImpl(const T& val, std::vector<u8>& buffer) { +    void WriteImpl(const T& val, boost::container::small_vector<u8, 0x200>& buffer) {          const size_t aligned_size = Common::AlignUp(sizeof(T), 4);          const size_t old_size = buffer.size();          buffer.resize(old_size + aligned_size); @@ -176,8 +177,8 @@ private:      }  private: -    std::vector<u8> m_data_buffer; -    std::vector<u8> m_object_buffer; +    boost::container::small_vector<u8, 0x200> m_data_buffer; +    boost::container::small_vector<u8, 0x200> m_object_buffer;  };  } // namespace Service::android diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index b2b5677c8..52494e0d9 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {                  OnMotionUpdate(port, type, id, value);              }},              .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }}, -            .on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) { -                OnAmiiboUpdate(port, type, amiibo_data); +            .on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) { +                OnAmiiboUpdate(port, type, tag_info);              }},              .on_camera_data = {[this, port](const std::vector<u8>& camera_data,                                              Joycon::IrsResolution format) { @@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c      return Common::Input::NfcState::Success;  }; +Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->StartNfcPolling()); +}; + +Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->StopNfcPolling()); +}; + +Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier, +                                                std::vector<u8>& out_data) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } +    return TranslateDriverResult(handle->ReadAmiiboData(out_data)); +} +  Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,                                                const std::vector<u8>& data) {      auto handle = GetHandle(identifier); -    if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { -        return Common::Input::NfcState::WriteFailed; +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown;      } -    return Common::Input::NfcState::Success; +    return TranslateDriverResult(handle->WriteNfcData(data)); +}; + +Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier, +                                                const Common::Input::MifareRequest& request, +                                                Common::Input::MifareRequest& data) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } + +    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); +    std::vector<Joycon::MifareReadChunk> read_request{}; +    for (const auto& request_data : request.data) { +        if (request_data.command == 0) { +            continue; +        } +        Joycon::MifareReadChunk chunk = { +            .command = command, +            .sector_key = {}, +            .sector = request_data.sector, +        }; +        memcpy(chunk.sector_key.data(), request_data.key.data(), +               sizeof(Joycon::MifareReadChunk::sector_key)); +        read_request.emplace_back(chunk); +    } + +    std::vector<Joycon::MifareReadData> read_data(read_request.size()); +    const auto result = handle->ReadMifareData(read_request, read_data); +    if (result == Joycon::DriverResult::Success) { +        for (std::size_t i = 0; i < read_request.size(); i++) { +            data.data[i] = { +                .command = static_cast<u8>(command), +                .sector = read_data[i].sector, +                .key = {}, +                .data = read_data[i].data, +            }; +        } +    } +    return TranslateDriverResult(result); +}; + +Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier, +                                                 const Common::Input::MifareRequest& request) { +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::NfcState::Unknown; +    } + +    const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command); +    std::vector<Joycon::MifareWriteChunk> write_request{}; +    for (const auto& request_data : request.data) { +        if (request_data.command == 0) { +            continue; +        } +        Joycon::MifareWriteChunk chunk = { +            .command = command, +            .sector_key = {}, +            .sector = request_data.sector, +            .data = {}, +        }; +        memcpy(chunk.sector_key.data(), request_data.key.data(), +               sizeof(Joycon::MifareReadChunk::sector_key)); +        memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data)); +        write_request.emplace_back(chunk); +    } + +    return TranslateDriverResult(handle->WriteMifareData(write_request));  };  Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, @@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {  }  void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, -                             const std::vector<u8>& amiibo_data) { +                             const Joycon::TagInfo& tag_info) {      const auto identifier = GetIdentifier(port, type); -    const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved -                                               : Common::Input::NfcState::NewAmiibo; -    SetNfc(identifier, {nfc_state, amiibo_data}); +    const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved +                                                     : Common::Input::NfcState::NewAmiibo; + +    const Common::Input::NfcStatus nfc_status{ +        .state = nfc_state, +        .uuid_length = tag_info.uuid_length, +        .protocol = tag_info.protocol, +        .tag_type = tag_info.tag_type, +        .uuid = tag_info.uuid, +    }; + +    SetNfc(identifier, nfc_status);  }  void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, @@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {          return "Unknown Switch Controller";      }  } + +Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const { +    switch (result) { +    case Joycon::DriverResult::Success: +        return Common::Input::NfcState::Success; +    case Joycon::DriverResult::Disabled: +        return Common::Input::NfcState::WrongDeviceState; +    case Joycon::DriverResult::NotSupported: +        return Common::Input::NfcState::NotSupported; +    default: +        return Common::Input::NfcState::Unknown; +    } +} +  } // namespace InputCommon diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index e3f0ad78f..4c323d7d6 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>;  struct Battery;  struct Color;  struct MotionData; +struct TagInfo;  enum class ControllerType : u8;  enum class DriverResult;  enum class IrsResolution; @@ -39,9 +40,18 @@ public:      Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,                                                  Common::Input::CameraFormat camera_format) override; -    Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; -    Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, +    Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override; +    Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override; +    Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override; +    Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier, +                                           std::vector<u8>& out_data) override; +    Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,                                           const std::vector<u8>& data) override; +    Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier, +                                           const Common::Input::MifareRequest& request, +                                           Common::Input::MifareRequest& out_data) override; +    Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier, +                                            const Common::Input::MifareRequest& request) override;      Common::Input::DriverResult SetPollingMode(          const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override; @@ -82,7 +92,7 @@ private:                          const Joycon::MotionData& value);      void OnRingConUpdate(f32 ring_data);      void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type, -                        const std::vector<u8>& amiibo_data); +                        const Joycon::TagInfo& amiibo_data);      void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,                          Joycon::IrsResolution format); @@ -102,6 +112,8 @@ private:      /// Returns the name of the device in text format      std::string JoyconName(Joycon::ControllerType type) const; +    Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const; +      std::jthread scan_thread;      // Joycon types are split by type to ease supporting dualjoycon configurations diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 6435b8af8..180eb53ef 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(      switch (polling_mode) {      case Common::Input::PollingMode::NFC: -        if (state == State::Initialized) { -            state = State::WaitingForAmiibo; -        } +        state = State::Initialized;          return Common::Input::DriverResult::Success;      default: -        if (state == State::AmiiboIsOpen) { +        if (state == State::TagNearby) {              CloseAmiibo();          } +        state = State::Disabled;          return Common::Input::DriverResult::NotSupported;      }  } @@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(      [[maybe_unused]] const PadIdentifier& identifier_) const {      return Common::Input::NfcState::Success;  } +Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) { +    if (state != State::Initialized) { +        return Common::Input::NfcState::WrongDeviceState; +    } +    state = State::WaitingForAmiibo; +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) { +    if (state == State::Disabled) { +        return Common::Input::NfcState::WrongDeviceState; +    } +    if (state == State::TagNearby) { +        CloseAmiibo(); +    } +    state = State::Initialized; +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_, +                                                      std::vector<u8>& out_data) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 1) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    out_data.resize(nfc_data.size()); +    memcpy(out_data.data(), nfc_data.data(), nfc_data.size()); +    return Common::Input::NfcState::Success; +}  Common::Input::NfcState VirtualAmiibo::WriteNfcData(      [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { @@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(      return Common::Input::NfcState::Success;  } +Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_, +                                                      const Common::Input::MifareRequest& request, +                                                      Common::Input::MifareRequest& out_data) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 6) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    for (std::size_t i = 0; i < request.data.size(); i++) { +        if (request.data[i].command == 0) { +            continue; +        } +        out_data.data[i].command = request.data[i].command; +        out_data.data[i].sector = request.data[i].sector; + +        const std::size_t sector_index = +            request.data[i].sector * sizeof(Common::Input::MifareData::data); + +        if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { +            return Common::Input::NfcState::WriteFailed; +        } + +        // Ignore the sector key as we don't support it +        memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index, +               sizeof(Common::Input::MifareData::data)); +    } + +    return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteMifareData( +    const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) { +    if (state != State::TagNearby) { +        return Common::Input::NfcState::WrongDeviceState; +    } + +    if (status.tag_type != 1U << 6) { +        return Common::Input::NfcState::InvalidTagType; +    } + +    for (std::size_t i = 0; i < request.data.size(); i++) { +        if (request.data[i].command == 0) { +            continue; +        } + +        const std::size_t sector_index = +            request.data[i].sector * sizeof(Common::Input::MifareData::data); + +        if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) { +            return Common::Input::NfcState::WriteFailed; +        } + +        // Ignore the sector key as we don't support it +        memcpy(nfc_data.data() + sector_index, request.data[i].data.data(), +               sizeof(Common::Input::MifareData::data)); +    } + +    return Common::Input::NfcState::Success; +} +  VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {      return state;  } @@ -112,23 +207,31 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {      case AmiiboSizeWithoutPassword:      case AmiiboSizeWithSignature:          nfc_data.resize(AmiiboSize); +        status.tag_type = 1U << 1; +        status.uuid_length = 7;          break;      case MifareSize:          nfc_data.resize(MifareSize); +        status.tag_type = 1U << 6; +        status.uuid_length = 4;          break;      default:          return Info::NotAnAmiibo;      } -    state = State::AmiiboIsOpen; +    status.uuid = {}; +    status.protocol = 1; +    state = State::TagNearby; +    status.state = Common::Input::NfcState::NewAmiibo,      memcpy(nfc_data.data(), data.data(), data.size_bytes()); -    SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); +    memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); +    SetNfc(identifier, status);      return Info::Success;  }  VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { -    if (state == State::AmiiboIsOpen) { -        SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); +    if (state == State::TagNearby) { +        SetNfc(identifier, status);          return Info::Success;      } @@ -136,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {  }  VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { -    state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo -                                                            : State::Initialized; -    SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); +    if (state != State::TagNearby) { +        return Info::Success; +    } + +    state = State::WaitingForAmiibo; +    status.state = Common::Input::NfcState::AmiiboRemoved; +    SetNfc(identifier, status); +    status.tag_type = 0;      return Info::Success;  } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 09ca09e68..490f38e05 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -20,9 +20,10 @@ namespace InputCommon {  class VirtualAmiibo final : public InputEngine {  public:      enum class State { +        Disabled,          Initialized,          WaitingForAmiibo, -        AmiiboIsOpen, +        TagNearby,      };      enum class Info { @@ -41,9 +42,17 @@ public:          const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;      Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; - +    Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override; +    Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override; +    Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_, +                                           std::vector<u8>& out_data) override;      Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,                                           const std::vector<u8>& data) override; +    Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_, +                                           const Common::Input::MifareRequest& data, +                                           Common::Input::MifareRequest& out_data) override; +    Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_, +                                            const Common::Input::MifareRequest& data) override;      State GetCurrentState() const; @@ -61,8 +70,9 @@ private:      static constexpr std::size_t MifareSize = 0x400;      std::string file_path{}; -    State state{State::Initialized}; +    State state{State::Disabled};      std::vector<u8> nfc_data; +    Common::Input::NfcStatus status;      Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};  };  } // namespace InputCommon diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 95106f16d..2c8c66951 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -2,6 +2,7 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "common/logging/log.h" +#include "common/scope_exit.h"  #include "common/swap.h"  #include "common/thread.h"  #include "input_common/helpers/joycon_driver.h" @@ -112,7 +113,7 @@ DriverResult JoyconDriver::InitializeDevice() {      joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,                                                     right_stick_calibration, motion_calibration); -    // Start pooling for data +    // Start polling for data      is_connected = true;      if (!input_thread_running) {          input_thread = @@ -208,7 +209,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {          joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());      } -    if (nfc_protocol->IsEnabled()) { +    if (nfc_protocol->IsPolling()) {          if (amiibo_detected) {              if (!nfc_protocol->HasAmiibo()) {                  joycon_poller->UpdateAmiibo({}); @@ -218,10 +219,10 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {          }          if (!amiibo_detected) { -            std::vector<u8> data(0x21C); -            const auto result = nfc_protocol->ScanAmiibo(data); +            Joycon::TagInfo tag_info; +            const auto result = nfc_protocol->GetTagInfo(tag_info);              if (result == DriverResult::Success) { -                joycon_poller->UpdateAmiibo(data); +                joycon_poller->UpdateAmiibo(tag_info);                  amiibo_detected = true;              }          } @@ -247,6 +248,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {  }  DriverResult JoyconDriver::SetPollingMode() { +    SCOPE_EXIT({ disable_input_thread = false; });      disable_input_thread = true;      rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); @@ -276,7 +278,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (irs_enabled && supported_features.irs) {          auto result = irs_protocol->EnableIrs();          if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          irs_protocol->DisableIrs(); @@ -286,10 +287,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (nfc_enabled && supported_features.nfc) {          auto result = nfc_protocol->EnableNfc();          if (result == DriverResult::Success) { -            result = nfc_protocol->StartNFCPollingMode(); -        } -        if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          nfc_protocol->DisableNfc(); @@ -303,7 +300,6 @@ DriverResult JoyconDriver::SetPollingMode() {          }          if (result == DriverResult::Success) {              ring_connected = true; -            disable_input_thread = false;              return result;          }          ring_connected = false; @@ -314,7 +310,6 @@ DriverResult JoyconDriver::SetPollingMode() {      if (passive_enabled && supported_features.passive) {          const auto result = generic_protocol->EnablePassiveMode();          if (result == DriverResult::Success) { -            disable_input_thread = false;              return result;          }          LOG_ERROR(Input, "Error enabling passive mode"); @@ -328,7 +323,6 @@ DriverResult JoyconDriver::SetPollingMode() {      // Switch calls this function after enabling active mode      generic_protocol->TriggersElapsed(); -    disable_input_thread = false;      return result;  } @@ -492,9 +486,63 @@ DriverResult JoyconDriver::SetRingConMode() {      return result;  } -DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +DriverResult JoyconDriver::StartNfcPolling() {      std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->StartNFCPollingMode(); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::StopNfcPolling() { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->StopNFCPollingMode(); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    out_data.resize(0x21C);      disable_input_thread = true; +    const auto result = nfc_protocol->ReadAmiibo(out_data); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +    std::scoped_lock lock{mutex};      if (!supported_features.nfc) {          return DriverResult::NotSupported; @@ -506,9 +554,51 @@ DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {          return DriverResult::ErrorWritingData;      } +    disable_input_thread = true;      const auto result = nfc_protocol->WriteAmiibo(data); +    disable_input_thread = false; +    return result; +} + +DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data, +                                          std::span<MifareReadData> out_data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->ReadMifare(data, out_data); +    disable_input_thread = false; + +    return result; +} + +DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    disable_input_thread = true; +    const auto result = nfc_protocol->WriteMifare(data);      disable_input_thread = false; +      return result;  } diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index e9b2fccbb..bc7025a21 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -49,7 +49,13 @@ public:      DriverResult SetIrMode();      DriverResult SetNfcMode();      DriverResult SetRingConMode(); +    DriverResult StartNfcPolling(); +    DriverResult StopNfcPolling(); +    DriverResult ReadAmiiboData(std::vector<u8>& out_data);      DriverResult WriteNfcData(std::span<const u8> data); +    DriverResult ReadMifareData(std::span<const MifareReadChunk> request, +                                std::span<MifareReadData> out_data); +    DriverResult WriteMifareData(std::span<const MifareWriteChunk> request);      void SetCallbacks(const JoyconCallbacks& callbacks); diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 5007b0e18..e0e431156 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -24,6 +24,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x  using MacAddress = std::array<u8, 6>;  using SerialNumber = std::array<u8, 15>;  using TagUUID = std::array<u8, 7>; +using MifareUUID = std::array<u8, 4>;  enum class ControllerType : u8 {      None = 0x00, @@ -307,6 +308,19 @@ enum class NFCStatus : u8 {      WriteDone = 0x05,      TagLost = 0x07,      WriteReady = 0x09, +    MifareDone = 0x10, +}; + +enum class MifareCmd : u8 { +    None = 0x00, +    Read = 0x30, +    AuthA = 0x60, +    AuthB = 0x61, +    Write = 0xA0, +    Transfer = 0xB0, +    Decrement = 0xC0, +    Increment = 0xC1, +    Store = 0xC2  };  enum class IrsMode : u8 { @@ -592,6 +606,14 @@ struct NFCWriteCommandData {  static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");  #pragma pack(pop) +struct MifareCommandData { +    u8 unknown1; +    u8 unknown2; +    u8 number_of_short_bytes; +    MifareUUID uid; +}; +static_assert(sizeof(MifareCommandData) == 0x7, "MifareCommandData is an invalid size"); +  struct NFCPollingCommandData {      u8 enable_mifare;      u8 unknown_1; @@ -629,6 +651,41 @@ struct NFCWritePackage {      std::array<NFCDataChunk, 4> data_chunks;  }; +struct MifareReadChunk { +    MifareCmd command; +    std::array<u8, 0x6> sector_key; +    u8 sector; +}; + +struct MifareWriteChunk { +    MifareCmd command; +    std::array<u8, 0x6> sector_key; +    u8 sector; +    std::array<u8, 0x10> data; +}; + +struct MifareReadData { +    u8 sector; +    std::array<u8, 0x10> data; +}; + +struct MifareReadPackage { +    MifareCommandData command_data; +    std::array<MifareReadChunk, 0x10> data_chunks; +}; + +struct MifareWritePackage { +    MifareCommandData command_data; +    std::array<MifareWriteChunk, 0x10> data_chunks; +}; + +struct TagInfo { +    u8 uuid_length; +    u8 protocol; +    u8 tag_type; +    std::array<u8, 10> uuid; +}; +  struct IrsConfigure {      MCUCommand command;      MCUSubCommand sub_command; @@ -744,7 +801,7 @@ struct JoyconCallbacks {      std::function<void(int, f32)> on_stick_data;      std::function<void(int, const MotionData&)> on_motion_data;      std::function<void(f32)> on_ring_data; -    std::function<void(const std::vector<u8>&)> on_amiibo_data; +    std::function<void(const Joycon::TagInfo&)> on_amiibo_data;      std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;  }; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index f7058c4a7..261f46255 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -40,6 +40,16 @@ DriverResult NfcProtocol::EnableNfc() {      if (result == DriverResult::Success) {          result = WaitUntilNfcIs(NFCStatus::Ready);      } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        is_enabled = true; +    }      return result;  } @@ -54,37 +64,50 @@ DriverResult NfcProtocol::DisableNfc() {      }      is_enabled = false; +    is_polling = false;      return result;  }  DriverResult NfcProtocol::StartNFCPollingMode() { -    LOG_DEBUG(Input, "Start NFC pooling Mode"); +    LOG_DEBUG(Input, "Start NFC polling Mode");      ScopedSetBlocking sb(this);      DriverResult result{DriverResult::Success};      if (result == DriverResult::Success) {          MCUCommandResponse output{}; -        result = SendStopPollingRequest(output); +        result = SendStartPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIs(NFCStatus::Ready); +        result = WaitUntilNfcIs(NFCStatus::Polling);      }      if (result == DriverResult::Success) { +        is_polling = true; +    } + +    return result; +} + +DriverResult NfcProtocol::StopNFCPollingMode() { +    LOG_DEBUG(Input, "Stop NFC polling Mode"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; + +    if (result == DriverResult::Success) {          MCUCommandResponse output{}; -        result = SendStartPollingRequest(output); +        result = SendStopPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIs(NFCStatus::Polling); +        result = WaitUntilNfcIs(NFCStatus::WriteReady);      }      if (result == DriverResult::Success) { -        is_enabled = true; +        is_polling = false;      }      return result;  } -DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) { +DriverResult NfcProtocol::GetTagInfo(Joycon::TagInfo& tag_info) {      if (update_counter++ < AMIIBO_UPDATE_DELAY) {          return DriverResult::Delayed;      } @@ -100,11 +123,41 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {      }      if (result == DriverResult::Success) { +        tag_info = { +            .uuid_length = tag_data.uuid_size, +            .protocol = 1, +            .tag_type = tag_data.type, +            .uuid = {}, +        }; + +        memcpy(tag_info.uuid.data(), tag_data.uuid.data(), tag_data.uuid_size); + +        // Investigate why mifare type is not correct +        if (tag_info.tag_type == 144) { +            tag_info.tag_type = 1U << 6; +        } +          std::string uuid_string;          for (auto& content : tag_data.uuid) {              uuid_string += fmt::format(" {:02x}", content);          }          LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string); +    } + +    return result; +} + +DriverResult NfcProtocol::ReadAmiibo(std::vector<u8>& data) { +    LOG_DEBUG(Input, "Scan for amiibos"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } + +    if (result == DriverResult::Success) {          result = GetAmiiboData(data);      } @@ -154,6 +207,69 @@ DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {      return result;  } +DriverResult NfcProtocol::ReadMifare(std::span<const MifareReadChunk> read_request, +                                     std::span<MifareReadData> out_data) { +    LOG_DEBUG(Input, "Read mifare"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; +    MifareUUID tag_uuid{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } +    if (result == DriverResult::Success) { +        memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); +        result = GetMifareData(tag_uuid, read_request, out_data); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStartPollingRequest(output, true); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteReady); +    } +    return result; +} + +DriverResult NfcProtocol::WriteMifare(std::span<const MifareWriteChunk> write_request) { +    LOG_DEBUG(Input, "Write mifare"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagFoundData tag_data{}; +    MifareUUID tag_uuid{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } +    if (result == DriverResult::Success) { +        memcpy(tag_uuid.data(), tag_data.uuid.data(), sizeof(MifareUUID)); +        result = WriteMifareData(tag_uuid, write_request); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStartPollingRequest(output, true); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteReady); +    } +    return result; +} +  bool NfcProtocol::HasAmiibo() {      if (update_counter++ < AMIIBO_UPDATE_DELAY) {          return true; @@ -341,6 +457,158 @@ DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<con      return result;  } +DriverResult NfcProtocol::GetMifareData(const MifareUUID& tag_uuid, +                                        std::span<const MifareReadChunk> read_request, +                                        std::span<MifareReadData> out_data) { +    constexpr std::size_t timeout_limit = 60; +    const auto nfc_data = MakeMifareReadPackage(tag_uuid, read_request); +    const std::vector<u8> nfc_buffer_data = SerializeMifareReadPackage(nfc_data); +    std::span<const u8> buffer(nfc_buffer_data); +    DriverResult result = DriverResult::Success; +    MCUCommandResponse output{}; +    u8 block_id = 1; +    u8 package_index = 0; +    std::size_t tries = 0; +    std::size_t current_position = 0; + +    LOG_INFO(Input, "Reading Mifare data"); + +    // Send data request. Nfc buffer size is 31, Send the data in smaller packages +    while (current_position < buffer.size() && tries++ < timeout_limit) { +        const std::size_t next_position = +            std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); +        const std::size_t block_size = next_position - current_position; +        const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + +        SendReadDataMifareRequest(output, block_id, is_last_packet, +                                  buffer.subspan(current_position, block_size)); + +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        // Increase position when data is confirmed by the joycon +        if (output.mcu_report == MCUReport::NFCState && +            (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && +            output.mcu_data[3] == block_id) { +            block_id++; +            current_position = next_position; +        } +    } + +    if (result != DriverResult::Success) { +        return result; +    } + +    // Wait for reply and save the output data +    while (tries++ < timeout_limit) { +        result = SendNextPackageRequest(output, package_index); +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (result != DriverResult::Success) { +            return result; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { +            constexpr std::size_t DATA_LENGHT = 0x10 + 1; +            constexpr std::size_t DATA_START = 11; +            const u8 number_of_elements = output.mcu_data[10]; +            for (std::size_t i = 0; i < number_of_elements; i++) { +                out_data[i].sector = output.mcu_data[DATA_START + (i * DATA_LENGHT)]; +                memcpy(out_data[i].data.data(), +                       output.mcu_data.data() + DATA_START + 1 + (i * DATA_LENGHT), +                       sizeof(MifareReadData::data)); +            } +            package_index++; +            continue; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { +            LOG_INFO(Input, "Finished reading mifare"); +            break; +        } +    } + +    return result; +} + +DriverResult NfcProtocol::WriteMifareData(const MifareUUID& tag_uuid, +                                          std::span<const MifareWriteChunk> write_request) { +    constexpr std::size_t timeout_limit = 60; +    const auto nfc_data = MakeMifareWritePackage(tag_uuid, write_request); +    const std::vector<u8> nfc_buffer_data = SerializeMifareWritePackage(nfc_data); +    std::span<const u8> buffer(nfc_buffer_data); +    DriverResult result = DriverResult::Success; +    MCUCommandResponse output{}; +    u8 block_id = 1; +    u8 package_index = 0; +    std::size_t tries = 0; +    std::size_t current_position = 0; + +    LOG_INFO(Input, "Writing Mifare data"); + +    // Send data request. Nfc buffer size is 31, Send the data in smaller packages +    while (current_position < buffer.size() && tries++ < timeout_limit) { +        const std::size_t next_position = +            std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); +        const std::size_t block_size = next_position - current_position; +        const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + +        SendReadDataMifareRequest(output, block_id, is_last_packet, +                                  buffer.subspan(current_position, block_size)); + +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        // Increase position when data is confirmed by the joycon +        if (output.mcu_report == MCUReport::NFCState && +            (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && +            output.mcu_data[3] == block_id) { +            block_id++; +            current_position = next_position; +        } +    } + +    if (result != DriverResult::Success) { +        return result; +    } + +    // Wait for reply and ignore the output data +    while (tries++ < timeout_limit) { +        result = SendNextPackageRequest(output, package_index); +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (result != DriverResult::Success) { +            return result; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        if (output.mcu_report == MCUReport::NFCState && output.mcu_data[1] == 0x10) { +            package_index++; +            continue; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::MifareDone) { +            LOG_INFO(Input, "Finished writing mifare"); +            break; +        } +    } + +    return result; +} +  DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,                                                    bool is_second_attempt) {      NFCRequestState request{ @@ -477,6 +745,28 @@ DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output,                         output);  } +DriverResult NfcProtocol::SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, +                                                    bool is_last_packet, std::span<const u8> data) { +    const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); +    NFCRequestState request{ +        .command_argument = NFCCommand::Mifare, +        .block_id = block_id, +        .packet_id = {}, +        .packet_flag = +            is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining, +        .data_length = static_cast<u8>(data_size), +        .raw_data = {}, +        .crc = {}, +    }; +    memcpy(request.raw_data.data(), data.data(), data_size); + +    std::array<u8, sizeof(NFCRequestState)> request_data{}; +    memcpy(request_data.data(), &request, sizeof(NFCRequestState)); +    request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); +    return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, +                       output); +} +  std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {      const std::size_t header_size =          sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); @@ -498,6 +788,48 @@ std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& packag      return serialized_data;  } +std::vector<u8> NfcProtocol::SerializeMifareReadPackage(const MifareReadPackage& package) const { +    const std::size_t header_size = sizeof(MifareCommandData); +    std::vector<u8> serialized_data(header_size); +    std::size_t start_index = 0; + +    memcpy(serialized_data.data(), &package, header_size); +    start_index += header_size; + +    for (const auto& data_chunk : package.data_chunks) { +        const std::size_t chunk_size = sizeof(MifareReadChunk); +        if (data_chunk.command == MifareCmd::None) { +            continue; +        } +        serialized_data.resize(start_index + chunk_size); +        memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); +        start_index += chunk_size; +    } + +    return serialized_data; +} + +std::vector<u8> NfcProtocol::SerializeMifareWritePackage(const MifareWritePackage& package) const { +    const std::size_t header_size = sizeof(MifareCommandData); +    std::vector<u8> serialized_data(header_size); +    std::size_t start_index = 0; + +    memcpy(serialized_data.data(), &package, header_size); +    start_index += header_size; + +    for (const auto& data_chunk : package.data_chunks) { +        const std::size_t chunk_size = sizeof(MifareWriteChunk); +        if (data_chunk.command == MifareCmd::None) { +            continue; +        } +        serialized_data.resize(start_index + chunk_size); +        memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); +        start_index += chunk_size; +    } + +    return serialized_data; +} +  NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,                                                      std::span<const u8> data) const {      return { @@ -527,6 +859,46 @@ NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,      };  } +MifareReadPackage NfcProtocol::MakeMifareReadPackage( +    const MifareUUID& tag_uuid, std::span<const MifareReadChunk> read_request) const { +    MifareReadPackage package{ +        .command_data{ +            .unknown1 = 0xd0, +            .unknown2 = 0x07, +            .number_of_short_bytes = static_cast<u8>( +                ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID)) / 2), +            .uid = tag_uuid, +        }, +        .data_chunks = {}, +    }; + +    for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { +        package.data_chunks[i] = read_request[i]; +    } + +    return package; +} + +MifareWritePackage NfcProtocol::MakeMifareWritePackage( +    const MifareUUID& tag_uuid, std::span<const MifareWriteChunk> read_request) const { +    MifareWritePackage package{ +        .command_data{ +            .unknown1 = 0xd0, +            .unknown2 = 0x07, +            .number_of_short_bytes = static_cast<u8>( +                ((read_request.size() * sizeof(MifareReadChunk)) + sizeof(MifareUUID) + 2) / 2), +            .uid = tag_uuid, +        }, +        .data_chunks = {}, +    }; + +    for (std::size_t i = 0; i < read_request.size() && i < package.data_chunks.size(); ++i) { +        package.data_chunks[i] = read_request[i]; +    } + +    return package; +} +  NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {      constexpr u8 NFC_PAGE_SIZE = 4; @@ -606,4 +978,8 @@ bool NfcProtocol::IsEnabled() const {      return is_enabled;  } +bool NfcProtocol::IsPolling() const { +    return is_polling; +} +  } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index eb58c427d..0be95e40e 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -25,14 +25,25 @@ public:      DriverResult StartNFCPollingMode(); -    DriverResult ScanAmiibo(std::vector<u8>& data); +    DriverResult StopNFCPollingMode(); + +    DriverResult GetTagInfo(Joycon::TagInfo& tag_info); + +    DriverResult ReadAmiibo(std::vector<u8>& data);      DriverResult WriteAmiibo(std::span<const u8> data); +    DriverResult ReadMifare(std::span<const MifareReadChunk> read_request, +                            std::span<MifareReadData> out_data); + +    DriverResult WriteMifare(std::span<const MifareWriteChunk> write_request); +      bool HasAmiibo();      bool IsEnabled() const; +    bool IsPolling() const; +  private:      // Number of times the function will be delayed until it outputs valid data      static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15; @@ -51,6 +62,13 @@ private:      DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); +    DriverResult GetMifareData(const MifareUUID& tag_uuid, +                               std::span<const MifareReadChunk> read_request, +                               std::span<MifareReadData> out_data); + +    DriverResult WriteMifareData(const MifareUUID& tag_uuid, +                                 std::span<const MifareWriteChunk> write_request); +      DriverResult SendStartPollingRequest(MCUCommandResponse& output,                                           bool is_second_attempt = false); @@ -65,17 +83,31 @@ private:      DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,                                              bool is_last_packet, std::span<const u8> data); +    DriverResult SendReadDataMifareRequest(MCUCommandResponse& output, u8 block_id, +                                           bool is_last_packet, std::span<const u8> data); +      std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; +    std::vector<u8> SerializeMifareReadPackage(const MifareReadPackage& package) const; + +    std::vector<u8> SerializeMifareWritePackage(const MifareWritePackage& package) const; +      NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;      NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; +    MifareReadPackage MakeMifareReadPackage(const MifareUUID& tag_uuid, +                                            std::span<const MifareReadChunk> read_request) const; + +    MifareWritePackage MakeMifareWritePackage(const MifareUUID& tag_uuid, +                                              std::span<const MifareWriteChunk> read_request) const; +      NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;      TagUUID GetTagUUID(std::span<const u8> data) const;      bool is_enabled{}; +    bool is_polling{};      std::size_t update_counter{};  }; diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index dca797f7a..1aab9e12a 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -70,8 +70,8 @@ void JoyconPoller::UpdateColor(const Color& color) {      callbacks.on_color_data(color);  } -void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) { -    callbacks.on_amiibo_data(amiibo_data); +void JoyconPoller::UpdateAmiibo(const Joycon::TagInfo& tag_info) { +    callbacks.on_amiibo_data(tag_info);  }  void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index 0fa72c6db..3746abe5d 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,8 +36,8 @@ public:      void UpdateColor(const Color& color);      void UpdateRing(s16 value, const RingStatus& ring_status); -    void UpdateAmiibo(const std::vector<u8>& amiibo_data); -    void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format); +    void UpdateAmiibo(const Joycon::TagInfo& tag_info); +    void UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format);  private:      void UpdateActiveLeftPadInput(const InputReportActive& input, diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 50b5a3dc8..c2d0cbb34 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -143,12 +143,46 @@ public:          return Common::Input::NfcState::NotSupported;      } +    // Start scanning for nfc tags +    virtual Common::Input::NfcState StartNfcPolling( +        [[maybe_unused]] const PadIdentifier& identifier_) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Start scanning for nfc tags +    virtual Common::Input::NfcState StopNfcPolling( +        [[maybe_unused]] const PadIdentifier& identifier_) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Reads data from amiibo tag +    virtual Common::Input::NfcState ReadAmiiboData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] std::vector<u8>& out_data) { +        return Common::Input::NfcState::NotSupported; +    } +      // Writes data to an nfc tag      virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,                                                   [[maybe_unused]] const std::vector<u8>& data) {          return Common::Input::NfcState::NotSupported;      } +    // Reads data from mifare tag +    virtual Common::Input::NfcState ReadMifareData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] const Common::Input::MifareRequest& request, +        [[maybe_unused]] Common::Input::MifareRequest& out_data) { +        return Common::Input::NfcState::NotSupported; +    } + +    // Write data to mifare tag +    virtual Common::Input::NfcState WriteMifareData( +        [[maybe_unused]] const PadIdentifier& identifier_, +        [[maybe_unused]] const Common::Input::MifareRequest& request) { +        return Common::Input::NfcState::NotSupported; +    } +      // Returns the engine name      [[nodiscard]] const std::string& GetEngineName() const; diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 380a01542..870e76ab0 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -792,8 +792,7 @@ public:          const Common::Input::CallbackStatus status{              .type = Common::Input::InputType::Nfc, -            .nfc_status = nfc_status.state, -            .raw_data = nfc_status.data, +            .nfc_status = nfc_status,          };          TriggerOnChange(status); @@ -836,10 +835,31 @@ public:          return input_engine->SupportsNfc(identifier);      } +    Common::Input::NfcState StartNfcPolling() { +        return input_engine->StartNfcPolling(identifier); +    } + +    Common::Input::NfcState StopNfcPolling() { +        return input_engine->StopNfcPolling(identifier); +    } + +    Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { +        return input_engine->ReadAmiiboData(identifier, out_data); +    } +      Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override {          return input_engine->WriteNfcData(identifier, data);      } +    Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, +                                           Common::Input::MifareRequest& out_data) { +        return input_engine->ReadMifareData(identifier, request, out_data); +    } + +    Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { +        return input_engine->WriteMifareData(identifier, request); +    } +  private:      const PadIdentifier identifier;      InputEngine* input_engine; diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index c3c2281bb..9ff4028c2 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -479,7 +479,7 @@ void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {          const u32 remainder{4 - element};          const TransformFeedbackVarying* xfb_varying{};          const size_t xfb_varying_index{base_index + element}; -        if (xfb_varying_index < runtime_info.xfb_varyings.size()) { +        if (xfb_varying_index < runtime_info.xfb_count) {              xfb_varying = &runtime_info.xfb_varyings[xfb_varying_index];              xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;          } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 0f86a8004..34592a01f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -387,7 +387,7 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr  }  void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) { -    if (ctx.runtime_info.xfb_varyings.empty()) { +    if (ctx.runtime_info.xfb_count == 0) {          return;      }      ctx.AddCapability(spv::Capability::TransformFeedback); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index fd15f47ea..bec5db173 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -160,7 +160,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo          const u32 remainder{4 - element};          const TransformFeedbackVarying* xfb_varying{};          const size_t xfb_varying_index{base_attr_index + element}; -        if (xfb_varying_index < ctx.runtime_info.xfb_varyings.size()) { +        if (xfb_varying_index < ctx.runtime_info.xfb_count) {              xfb_varying = &ctx.runtime_info.xfb_varyings[xfb_varying_index];              xfb_varying = xfb_varying->components > 0 ? xfb_varying : nullptr;          } diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 3b63c249f..619c0b138 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -84,7 +84,8 @@ struct RuntimeInfo {      bool glasm_use_storage_buffers{};      /// Transform feedback state for each varying -    std::vector<TransformFeedbackVarying> xfb_varyings; +    std::array<TransformFeedbackVarying, 256> xfb_varyings{}; +    u32 xfb_count{0};  };  } // namespace Shader diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 45977d578..58a45ab67 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -207,7 +207,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am      if (has_new_downloads) {          memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);      } -    tmp_buffer.resize(amount); +    tmp_buffer.resize_destructive(amount);      cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);      cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);      return true; @@ -1279,7 +1279,7 @@ template <class P>  typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu_addr,                                                                         u32 wanted_size) {      static constexpr int STREAM_LEAP_THRESHOLD = 16; -    std::vector<BufferId> overlap_ids; +    boost::container::small_vector<BufferId, 16> overlap_ids;      VAddr begin = cpu_addr;      VAddr end = cpu_addr + wanted_size;      int stream_score = 0; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 63a120f7a..fe6068cfe 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -229,7 +229,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf      using OverlapCounter = boost::icl::split_interval_map<VAddr, int>;      struct OverlapResult { -        std::vector<BufferId> ids; +        boost::container::small_vector<BufferId, 16> ids;          VAddr begin;          VAddr end;          bool has_stream_leap = false; @@ -582,7 +582,7 @@ private:      BufferId inline_buffer_id;      std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table; -    std::vector<u8> tmp_buffer; +    Common::ScratchBuffer<u8> tmp_buffer;  };  } // namespace VideoCommon diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index 83112dfce..7d660af47 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h @@ -63,7 +63,6 @@ struct ChCommand {  };  using ChCommandHeaderList = std::vector<ChCommandHeader>; -using ChCommandList = std::vector<ChCommand>;  struct ThiRegisters {      u32_le increment_syncpt{}; diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 1cdb690ed..8a2784cdc 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h @@ -6,6 +6,7 @@  #include <array>  #include <span>  #include <vector> +#include <boost/container/small_vector.hpp>  #include <queue>  #include "common/bit_field.h" @@ -102,11 +103,12 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub  struct CommandList final {      CommandList() = default;      explicit CommandList(std::size_t size) : command_lists(size) {} -    explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_) +    explicit CommandList( +        boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)          : prefetch_command_list{std::move(prefetch_command_list_)} {} -    std::vector<CommandListHeader> command_lists; -    std::vector<CommandHeader> prefetch_command_list; +    boost::container::small_vector<CommandListHeader, 512> command_lists; +    boost::container::small_vector<CommandHeader, 512> prefetch_command_list;  };  /** diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index ebe5536de..bc1eb41e7 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -108,9 +108,11 @@ void MaxwellDMA::Launch() {          if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {              ASSERT(regs.remap_const.component_size_minus_one == 3);              accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value); -            std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); +            read_buffer.resize_destructive(regs.line_length_in * sizeof(u32)); +            std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in); +            std::ranges::fill(span, regs.remap_consta_value);              memory_manager.WriteBlockUnsafe(regs.offset_out, -                                            reinterpret_cast<u8*>(tmp_buffer.data()), +                                            reinterpret_cast<u8*>(read_buffer.data()),                                              regs.line_length_in * sizeof(u32));          } else {              memory_manager.FlushCaching(); @@ -126,32 +128,32 @@ void MaxwellDMA::Launch() {                  UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);                  UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);                  UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); -                std::vector<u8> tmp_buffer(16); +                read_buffer.resize_destructive(16);                  for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {                      memory_manager.ReadBlockUnsafe(                          convert_linear_2_blocklinear_addr(regs.offset_in + offset), -                        tmp_buffer.data(), tmp_buffer.size()); -                    memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(), -                                                    tmp_buffer.size()); +                        read_buffer.data(), read_buffer.size()); +                    memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(), +                                                    read_buffer.size());                  }              } else if (is_src_pitch && !is_dst_pitch) {                  UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);                  UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);                  UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); -                std::vector<u8> tmp_buffer(16); +                read_buffer.resize_destructive(16);                  for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { -                    memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(), -                                                   tmp_buffer.size()); +                    memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), +                                                   read_buffer.size());                      memory_manager.WriteBlockCached(                          convert_linear_2_blocklinear_addr(regs.offset_out + offset), -                        tmp_buffer.data(), tmp_buffer.size()); +                        read_buffer.data(), read_buffer.size());                  }              } else {                  if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { -                    std::vector<u8> tmp_buffer(regs.line_length_in); -                    memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), +                    read_buffer.resize_destructive(regs.line_length_in); +                    memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(),                                                     regs.line_length_in); -                    memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(), +                    memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),                                                      regs.line_length_in);                  }              } @@ -171,7 +173,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {      src_operand.address = regs.offset_in;      DMA::BufferOperand dst_operand; -    dst_operand.pitch = regs.pitch_out; +    u32 abs_pitch_out = std::abs(static_cast<s32>(regs.pitch_out)); +    dst_operand.pitch = abs_pitch_out;      dst_operand.width = regs.line_length_in;      dst_operand.height = regs.line_count;      dst_operand.address = regs.offset_out; @@ -218,7 +221,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {      const size_t src_size =          CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); -    const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; +    const size_t dst_size = static_cast<size_t>(abs_pitch_out) * regs.line_count;      read_buffer.resize_destructive(src_size);      write_buffer.resize_destructive(dst_size); @@ -227,7 +230,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {      UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,                       src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, -                     regs.pitch_out); +                     abs_pitch_out);      memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);  } diff --git a/src/video_core/host1x/codecs/h264.cpp b/src/video_core/host1x/codecs/h264.cpp index 6ce179167..ce827eb6c 100644 --- a/src/video_core/host1x/codecs/h264.cpp +++ b/src/video_core/host1x/codecs/h264.cpp @@ -4,6 +4,7 @@  #include <array>  #include <bit> +#include "common/scratch_buffer.h"  #include "common/settings.h"  #include "video_core/host1x/codecs/h264.h"  #include "video_core/host1x/host1x.h" @@ -188,7 +189,8 @@ void H264BitWriter::WriteBit(bool state) {  }  void H264BitWriter::WriteScalingList(std::span<const u8> list, s32 start, s32 count) { -    std::vector<u8> scan(count); +    static Common::ScratchBuffer<u8> scan{}; +    scan.resize_destructive(count);      if (count == 16) {          std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());      } else { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index b2f7e160a..45141e488 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -587,7 +587,7 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,  void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,                                VideoCommon::CacheType which) { -    std::vector<u8> tmp_buffer(size); +    tmp_buffer.resize_destructive(size);      ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which);      // The output block must be flushed in case it has data modified from the GPU. @@ -670,9 +670,9 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons      return result;  } -std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange( -    GPUVAddr gpu_addr, std::size_t size) const { -    std::vector<std::pair<GPUVAddr, std::size_t>> result{}; +boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> +MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const { +    boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> result{};      GetSubmappedRangeImpl<true>(gpu_addr, size, result);      return result;  } @@ -680,8 +680,9 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(  template <bool is_gpu_address>  void MemoryManager::GetSubmappedRangeImpl(      GPUVAddr gpu_addr, std::size_t size, -    std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& -        result) const { +    boost::container::small_vector< +        std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>& result) +    const {      std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>          last_segment{};      std::optional<VAddr> old_page_addr{}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 794535122..4202c26ff 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -8,10 +8,12 @@  #include <mutex>  #include <optional>  #include <vector> +#include <boost/container/small_vector.hpp>  #include "common/common_types.h"  #include "common/multi_level_page_table.h"  #include "common/range_map.h" +#include "common/scratch_buffer.h"  #include "common/virtual_buffer.h"  #include "video_core/cache_types.h"  #include "video_core/pte_kind.h" @@ -107,8 +109,8 @@ public:       * if the region is continuous, a single pair will be returned. If it's unmapped, an empty       * vector will be returned;       */ -    std::vector<std::pair<GPUVAddr, std::size_t>> GetSubmappedRange(GPUVAddr gpu_addr, -                                                                    std::size_t size) const; +    boost::container::small_vector<std::pair<GPUVAddr, std::size_t>, 32> GetSubmappedRange( +        GPUVAddr gpu_addr, std::size_t size) const;      GPUVAddr Map(GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size,                   PTEKind kind = PTEKind::INVALID, bool is_big_pages = true); @@ -165,7 +167,8 @@ private:      template <bool is_gpu_address>      void GetSubmappedRangeImpl(          GPUVAddr gpu_addr, std::size_t size, -        std::vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>>& +        boost::container::small_vector< +            std::pair<std::conditional_t<is_gpu_address, GPUVAddr, VAddr>, std::size_t>, 32>&              result) const;      Core::System& system; @@ -215,8 +218,8 @@ private:      Common::VirtualBuffer<u32> big_page_table_cpu;      std::vector<u64> big_page_continuous; -    std::vector<std::pair<VAddr, std::size_t>> page_stash{}; -    std::vector<std::pair<VAddr, std::size_t>> page_stash2{}; +    boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash{}; +    boost::container::small_vector<std::pair<VAddr, std::size_t>, 32> page_stash2{};      mutable std::mutex guard; @@ -226,6 +229,8 @@ private:      std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;      static std::atomic<size_t> unique_identifier_generator; + +    Common::ScratchBuffer<u8> tmp_buffer;  };  } // namespace Tegra diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 3f077311e..0329ed820 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -85,7 +85,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,      case Shader::Stage::VertexB:      case Shader::Stage::Geometry:          if (!use_assembly_shaders && key.xfb_enabled != 0) { -            info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); +            auto [varyings, count] = VideoCommon::MakeTransformFeedbackVaryings(key.xfb_state); +            info.xfb_varyings = varyings; +            info.xfb_count = count;          }          break;      case Shader::Stage::TessellationEval: diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index e30fcb1ed..f47301ad5 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -361,7 +361,7 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,          .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,      };      // Measuring a popular game, this number never exceeds the specified size once data is warmed up -    boost::container::small_vector<VkBufferCopy, 3> vk_copies(copies.size()); +    boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());      std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy);      scheduler.RequestOutsideRenderPassOperationContext();      scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a2cfb2105..9f316113c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -167,7 +167,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program                  info.fixed_state_point_size = point_size;              }              if (key.state.xfb_enabled) { -                info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); +                auto [varyings, count] = +                    VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); +                info.xfb_varyings = varyings; +                info.xfb_count = count;              }              info.convert_depth_mode = gl_ndc;          } @@ -214,7 +217,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program              info.fixed_state_point_size = point_size;          }          if (key.state.xfb_enabled != 0) { -            info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); +            auto [varyings, count] = +                VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state); +            info.xfb_varyings = varyings; +            info.xfb_count = count;          }          info.convert_depth_mode = gl_ndc;          break; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f025f618b..f3cef09dd 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -330,9 +330,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {      };  } -[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( -    std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { -    std::vector<VkBufferCopy> result(copies.size()); +[[maybe_unused]] [[nodiscard]] boost::container::small_vector<VkBufferCopy, 16> +TransformBufferCopies(std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { +    boost::container::small_vector<VkBufferCopy, 16> result(copies.size());      std::ranges::transform(          copies, result.begin(), [buffer_offset](const VideoCommon::BufferCopy& copy) {              return VkBufferCopy{ @@ -344,7 +344,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {      return result;  } -[[nodiscard]] std::vector<VkBufferImageCopy> TransformBufferImageCopies( +[[nodiscard]] boost::container::small_vector<VkBufferImageCopy, 16> TransformBufferImageCopies(      std::span<const BufferImageCopy> copies, size_t buffer_offset, VkImageAspectFlags aspect_mask) {      struct Maker {          VkBufferImageCopy operator()(const BufferImageCopy& copy) const { @@ -377,14 +377,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {          VkImageAspectFlags aspect_mask;      };      if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) { -        std::vector<VkBufferImageCopy> result(copies.size() * 2); +        boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size() * 2);          std::ranges::transform(copies, result.begin(),                                 Maker{buffer_offset, VK_IMAGE_ASPECT_DEPTH_BIT});          std::ranges::transform(copies, result.begin() + copies.size(),                                 Maker{buffer_offset, VK_IMAGE_ASPECT_STENCIL_BIT});          return result;      } else { -        std::vector<VkBufferImageCopy> result(copies.size()); +        boost::container::small_vector<VkBufferImageCopy, 16> result(copies.size());          std::ranges::transform(copies, result.begin(), Maker{buffer_offset, aspect_mask});          return result;      } @@ -867,8 +867,8 @@ void TextureCacheRuntime::BarrierFeedbackLoop() {  void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,                                             std::span<const VideoCommon::ImageCopy> copies) { -    std::vector<VkBufferImageCopy> vk_in_copies(copies.size()); -    std::vector<VkBufferImageCopy> vk_out_copies(copies.size()); +    boost::container::small_vector<VkBufferImageCopy, 16> vk_in_copies(copies.size()); +    boost::container::small_vector<VkBufferImageCopy, 16> vk_out_copies(copies.size());      const VkImageAspectFlags src_aspect_mask = src.AspectMask();      const VkImageAspectFlags dst_aspect_mask = dst.AspectMask(); @@ -1157,7 +1157,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im  void TextureCacheRuntime::CopyImage(Image& dst, Image& src,                                      std::span<const VideoCommon::ImageCopy> copies) { -    std::vector<VkImageCopy> vk_copies(copies.size()); +    boost::container::small_vector<VkImageCopy, 16> vk_copies(copies.size());      const VkImageAspectFlags aspect_mask = dst.AspectMask();      ASSERT(aspect_mask == src.AspectMask()); @@ -1332,7 +1332,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,          ScaleDown(true);      }      scheduler->RequestOutsideRenderPassOperationContext(); -    std::vector vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); +    auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);      const VkBuffer src_buffer = buffer;      const VkImage vk_image = *original_image;      const VkImageAspectFlags vk_aspect_mask = aspect_mask; @@ -1367,8 +1367,9 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS      if (is_rescaled) {          ScaleDown();      } -    boost::container::small_vector<VkBuffer, 1> buffers_vector{}; -    boost::container::small_vector<std::vector<VkBufferImageCopy>, 1> vk_copies; +    boost::container::small_vector<VkBuffer, 8> buffers_vector{}; +    boost::container::small_vector<boost::container::small_vector<VkBufferImageCopy, 16>, 8> +        vk_copies;      for (size_t index = 0; index < buffers_span.size(); index++) {          buffers_vector.emplace_back(buffers_span[index]);          vk_copies.emplace_back( @@ -1858,7 +1859,7 @@ Framebuffer::~Framebuffer() = default;  void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,                                      std::span<ImageView*, NUM_RT> color_buffers,                                      ImageView* depth_buffer, bool is_rescaled) { -    std::vector<VkImageView> attachments; +    boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;      RenderPassKey renderpass_key{};      s32 num_layers = 1; diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index c5213875b..4db948b6d 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -151,11 +151,9 @@ void ShaderCache::RemovePendingShaders() {      marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),                               marked_for_removal.end()); -    std::vector<ShaderInfo*> removed_shaders; -    removed_shaders.reserve(marked_for_removal.size()); +    boost::container::small_vector<ShaderInfo*, 16> removed_shaders;      std::scoped_lock lock{lookup_mutex}; -      for (Entry* const entry : marked_for_removal) {          removed_shaders.push_back(entry->data); diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 1b8a17ee8..55d49d017 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -6,6 +6,7 @@  #include <array>  #include <optional>  #include <vector> +#include <boost/container/small_vector.hpp>  #include "common/common_funcs.h"  #include "common/common_types.h" @@ -108,8 +109,8 @@ struct ImageBase {      std::vector<ImageViewInfo> image_view_infos;      std::vector<ImageViewId> image_view_ids; -    std::vector<u32> slice_offsets; -    std::vector<SubresourceBase> slice_subresources; +    boost::container::small_vector<u32, 16> slice_offsets; +    boost::container::small_vector<SubresourceBase, 16> slice_subresources;      std::vector<AliasedImage> aliased_images;      std::vector<ImageId> overlapping_images; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index d58bb69ff..d3f03a995 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -526,7 +526,7 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {  template <class P>  void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { -    std::vector<ImageId> images; +    boost::container::small_vector<ImageId, 16> images;      ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {          if (!image.IsSafeDownload()) {              return; @@ -579,7 +579,7 @@ std::optional<VideoCore::RasterizerDownloadArea> TextureCache<P>::GetFlushArea(V  template <class P>  void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) { -    std::vector<ImageId> deleted_images; +    boost::container::small_vector<ImageId, 16> deleted_images;      ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });      for (const ImageId id : deleted_images) {          Image& image = slot_images[id]; @@ -593,7 +593,7 @@ void TextureCache<P>::UnmapMemory(VAddr cpu_addr, size_t size) {  template <class P>  void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size) { -    std::vector<ImageId> deleted_images; +    boost::container::small_vector<ImageId, 16> deleted_images;      ForEachImageInRegionGPU(as_id, gpu_addr, size,                              [&](ImageId id, Image&) { deleted_images.push_back(id); });      for (const ImageId id : deleted_images) { @@ -1101,7 +1101,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,      const bool native_bgr = runtime.HasNativeBgr();      const bool flexible_formats = True(options & RelaxedOptions::Format);      ImageId image_id{}; -    boost::container::small_vector<ImageId, 1> image_ids; +    boost::container::small_vector<ImageId, 8> image_ids;      const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {          if (True(existing_image.flags & ImageFlagBits::Remapped)) {              return false; @@ -1622,7 +1622,7 @@ ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr)          }      }      ImageId image_id{}; -    boost::container::small_vector<ImageId, 1> image_ids; +    boost::container::small_vector<ImageId, 8> image_ids;      const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {          if (True(existing_image.flags & ImageFlagBits::Remapped)) {              return false; @@ -1942,7 +1942,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {          image.map_view_id = map_id;          return;      } -    std::vector<ImageViewId> sparse_maps{}; +    boost::container::small_vector<ImageViewId, 16> sparse_maps;      ForEachSparseSegment(          image, [this, image_id, &sparse_maps](GPUVAddr gpu_addr, VAddr cpu_addr, size_t size) {              auto map_id = slot_map_views.insert(gpu_addr, cpu_addr, size, image_id); @@ -2217,7 +2217,7 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept {  template <class P>  void TextureCache<P>::SynchronizeAliases(ImageId image_id) { -    boost::container::small_vector<const AliasedImage*, 1> aliased_images; +    boost::container::small_vector<const AliasedImage*, 8> aliased_images;      Image& image = slot_images[image_id];      bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);      bool any_modified = True(image.flags & ImageFlagBits::GpuModified); diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 44232b961..e9ec91265 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -56,7 +56,7 @@ struct ImageViewInOut {  struct AsyncDecodeContext {      ImageId image_id;      Common::ScratchBuffer<u8> decoded_data; -    std::vector<BufferImageCopy> copies; +    boost::container::small_vector<BufferImageCopy, 16> copies;      std::mutex mutex;      std::atomic_bool complete;  }; @@ -429,7 +429,7 @@ private:      std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;      std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table; -    std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; +    std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;      VAddr virtual_invalid_space{}; diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 95a5b47d8..f781cb7a0 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -329,13 +329,13 @@ template <u32 GOB_EXTENT>  [[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress3D(      const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { -    const std::vector<u32> slice_offsets = CalculateSliceOffsets(new_info); +    const auto slice_offsets = CalculateSliceOffsets(new_info);      const u32 diff = static_cast<u32>(overlap.gpu_addr - gpu_addr);      const auto it = std::ranges::find(slice_offsets, diff);      if (it == slice_offsets.end()) {          return std::nullopt;      } -    const std::vector subresources = CalculateSliceSubresources(new_info); +    const auto subresources = CalculateSliceSubresources(new_info);      const SubresourceBase base = subresources[std::distance(slice_offsets.begin(), it)];      const ImageInfo& info = overlap.info;      if (!IsBlockLinearSizeCompatible(new_info, info, base.level, 0, strict_size)) { @@ -655,9 +655,9 @@ LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {      return sizes;  } -std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) { +boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info) {      ASSERT(info.type == ImageType::e3D); -    std::vector<u32> offsets; +    boost::container::small_vector<u32, 16> offsets;      offsets.reserve(NumSlices(info));      const LevelInfo level_info = MakeLevelInfo(info); @@ -679,9 +679,10 @@ std::vector<u32> CalculateSliceOffsets(const ImageInfo& info) {      return offsets;  } -std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info) { +boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources( +    const ImageInfo& info) {      ASSERT(info.type == ImageType::e3D); -    std::vector<SubresourceBase> subresources; +    boost::container::small_vector<SubresourceBase, 16> subresources;      subresources.reserve(NumSlices(info));      for (s32 level = 0; level < info.resources.levels; ++level) {          const s32 depth = AdjustMipSize(info.size.depth, level); @@ -723,8 +724,10 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {      }  } -std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, -                                             SubresourceBase base, u32 up_scale, u32 down_shift) { +boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies(const ImageInfo& dst, +                                                                    const ImageInfo& src, +                                                                    SubresourceBase base, +                                                                    u32 up_scale, u32 down_shift) {      ASSERT(dst.resources.levels >= src.resources.levels);      const bool is_dst_3d = dst.type == ImageType::e3D; @@ -733,7 +736,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn          ASSERT(src.resources.levels == 1);      }      const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D}; -    std::vector<ImageCopy> copies; +    boost::container::small_vector<ImageCopy, 16> copies;      copies.reserve(src.resources.levels);      for (s32 level = 0; level < src.resources.levels; ++level) {          ImageCopy& copy = copies.emplace_back(); @@ -770,9 +773,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn      return copies;  } -std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale, -                                                  u32 down_shift) { -    std::vector<ImageCopy> copies; +boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies(const ImageInfo& src, +                                                                         u32 up_scale, +                                                                         u32 down_shift) { +    boost::container::small_vector<ImageCopy, 16> copies;      copies.reserve(src.resources.levels);      const bool is_3d = src.type == ImageType::e3D;      for (s32 level = 0; level < src.resources.levels; ++level) { @@ -824,9 +828,11 @@ bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config      return gpu_memory.GpuToCpuAddress(address, guest_size_bytes).has_value();  } -std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, -                                            const ImageInfo& info, std::span<const u8> input, -                                            std::span<u8> output) { +boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::MemoryManager& gpu_memory, +                                                                   GPUVAddr gpu_addr, +                                                                   const ImageInfo& info, +                                                                   std::span<const u8> input, +                                                                   std::span<u8> output) {      const size_t guest_size_bytes = input.size_bytes();      const u32 bpp_log2 = BytesPerBlockLog2(info.format);      const Extent3D size = info.size; @@ -861,7 +867,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP                                              info.tile_width_spacing);      size_t guest_offset = 0;      u32 host_offset = 0; -    std::vector<BufferImageCopy> copies(num_levels); +    boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);      for (s32 level = 0; level < num_levels; ++level) {          const Extent3D level_size = AdjustMipSize(size, level); @@ -978,7 +984,7 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8      }  } -std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) { +boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies(const ImageInfo& info) {      const Extent3D size = info.size;      const u32 bytes_per_block = BytesPerBlock(info.format);      if (info.type == ImageType::Linear) { @@ -1006,7 +1012,7 @@ std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info) {      u32 host_offset = 0; -    std::vector<BufferImageCopy> copies(num_levels); +    boost::container::small_vector<BufferImageCopy, 16> copies(num_levels);      for (s32 level = 0; level < num_levels; ++level) {          const Extent3D level_size = AdjustMipSize(size, level);          const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); @@ -1042,10 +1048,10 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) {      return AdjustMipBlockSize(num_tiles, level_info.block, level);  } -std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) { +boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const ImageInfo& info) {      const Extent2D tile_size = DefaultBlockSize(info.format);      if (info.type == ImageType::Linear) { -        return std::vector{SwizzleParameters{ +        return {SwizzleParameters{              .num_tiles = AdjustTileSize(info.size, tile_size),              .block = {},              .buffer_offset = 0, @@ -1057,7 +1063,7 @@ std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info) {      const s32 num_levels = info.resources.levels;      u32 guest_offset = 0; -    std::vector<SwizzleParameters> params(num_levels); +    boost::container::small_vector<SwizzleParameters, 16> params(num_levels);      for (s32 level = 0; level < num_levels; ++level) {          const Extent3D level_size = AdjustMipSize(size, level);          const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index 84aa6880d..ab45a43c4 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -5,6 +5,7 @@  #include <optional>  #include <span> +#include <boost/container/small_vector.hpp>  #include "common/common_types.h"  #include "common/scratch_buffer.h" @@ -40,9 +41,10 @@ struct OverlapResult {  [[nodiscard]] LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept; -[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<u32, 16> CalculateSliceOffsets(const ImageInfo& info); -[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<SubresourceBase, 16> CalculateSliceSubresources( +    const ImageInfo& info);  [[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level); @@ -51,21 +53,18 @@ struct OverlapResult {  [[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept; -[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, -                                                           const ImageInfo& src, -                                                           SubresourceBase base, u32 up_scale = 1, -                                                           u32 down_shift = 0); +[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeShrinkImageCopies( +    const ImageInfo& dst, const ImageInfo& src, SubresourceBase base, u32 up_scale = 1, +    u32 down_shift = 0); -[[nodiscard]] std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, -                                                                u32 up_scale = 1, -                                                                u32 down_shift = 0); +[[nodiscard]] boost::container::small_vector<ImageCopy, 16> MakeReinterpretImageCopies( +    const ImageInfo& src, u32 up_scale = 1, u32 down_shift = 0);  [[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); -[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, -                                                          GPUVAddr gpu_addr, const ImageInfo& info, -                                                          std::span<const u8> input, -                                                          std::span<u8> output); +[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage( +    Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, +    std::span<const u8> input, std::span<u8> output);  [[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,                                            const ImageBase& image, std::span<u8> output); @@ -73,13 +72,15 @@ struct OverlapResult {  void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,                    std::span<BufferImageCopy> copies); -[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<BufferImageCopy, 16> FullDownloadCopies( +    const ImageInfo& info);  [[nodiscard]] Extent3D MipSize(Extent3D size, u32 level);  [[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level); -[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info); +[[nodiscard]] boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles( +    const ImageInfo& info);  void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,                    std::span<const BufferImageCopy> copies, std::span<const u8> memory, diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 155599316..1f353d2df 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -13,7 +13,7 @@  namespace VideoCommon { -std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( +std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(      const TransformFeedbackState& state) {      static constexpr std::array VECTORS{          28U,  // gl_Position @@ -62,7 +62,8 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(          216U, // gl_TexCoord[6]          220U, // gl_TexCoord[7]      }; -    std::vector<Shader::TransformFeedbackVarying> xfb(256); +    std::array<Shader::TransformFeedbackVarying, 256> xfb{}; +    u32 count{0};      for (size_t buffer = 0; buffer < state.layouts.size(); ++buffer) {          const auto& locations = state.varyings[buffer];          const auto& layout = state.layouts[buffer]; @@ -103,11 +104,12 @@ std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(                  }              }              xfb[attribute] = varying; +            count = std::max(count, attribute);              highest = std::max(highest, (base_offset + varying.components) * 4);          }          UNIMPLEMENTED_IF(highest != layout.stride);      } -    return xfb; +    return {xfb, count + 1};  }  } // namespace VideoCommon diff --git a/src/video_core/transform_feedback.h b/src/video_core/transform_feedback.h index d13eb16c3..401b1352a 100644 --- a/src/video_core/transform_feedback.h +++ b/src/video_core/transform_feedback.h @@ -24,7 +24,7 @@ struct TransformFeedbackState {          varyings;  }; -std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( +std::pair<std::array<Shader::TransformFeedbackVarying, 256>, u32> MakeTransformFeedbackVaryings(      const TransformFeedbackState& state);  } // namespace VideoCommon diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index fa9cde75b..b11abe311 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -316,6 +316,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,  std::vector<const char*> ExtensionListForVulkan(      const std::set<std::string, std::less<>>& extensions) {      std::vector<const char*> output; +    output.reserve(extensions.size());      for (const auto& extension : extensions) {          output.push_back(extension.c_str());      } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8768a7c3c..45a39451d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3840,7 +3840,7 @@ void GMainWindow::OnLoadAmiibo() {      auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();      // Remove amiibo if one is connected -    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { +    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {          virtual_amiibo->CloseAmiibo();          QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));          return; @@ -3868,7 +3868,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {      auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();      const QString title = tr("Error loading Amiibo data");      // Remove amiibo if one is connected -    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::AmiiboIsOpen) { +    if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {          virtual_amiibo->CloseAmiibo();          QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));          return; | 
