diff options
29 files changed, 239 insertions, 122 deletions
diff --git a/externals/SDL b/externals/SDL | |||
| Subproject c27f3ead7c37bcbef608f385baa9fce7232efc6 | Subproject 491fba1d06a4810645092b2559b9cc94abeb23b | ||
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx | |||
| Subproject 8c272f21d19c6e821345fd055f41b9640f9189d | Subproject 212afa2394a74226dcf1b7996a570aae17debb6 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt index dd6c895fd..f54dccc69 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt | |||
| @@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager | |||
| 29 | import org.yuzu.yuzu_emu.model.Game | 29 | import org.yuzu.yuzu_emu.model.Game |
| 30 | import org.yuzu.yuzu_emu.model.GamesViewModel | 30 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| 31 | import org.yuzu.yuzu_emu.model.HomeViewModel | 31 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 32 | import org.yuzu.yuzu_emu.utils.FileUtil | ||
| 33 | 32 | ||
| 34 | class SearchFragment : Fragment() { | 33 | class SearchFragment : Fragment() { |
| 35 | private var _binding: FragmentSearchBinding? = null | 34 | private var _binding: FragmentSearchBinding? = null |
| @@ -128,10 +127,7 @@ class SearchFragment : Fragment() { | |||
| 128 | 127 | ||
| 129 | R.id.chip_homebrew -> baseList.filter { it.isHomebrew } | 128 | R.id.chip_homebrew -> baseList.filter { it.isHomebrew } |
| 130 | 129 | ||
| 131 | R.id.chip_retail -> baseList.filter { | 130 | R.id.chip_retail -> baseList.filter { !it.isHomebrew } |
| 132 | FileUtil.hasExtension(it.path, "xci") || | ||
| 133 | FileUtil.hasExtension(it.path, "nsp") | ||
| 134 | } | ||
| 135 | 131 | ||
| 136 | else -> baseList | 132 | else -> baseList |
| 137 | } | 133 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index 6a048e39f..6527c64ab 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt | |||
| @@ -43,7 +43,7 @@ class Game( | |||
| 43 | 43 | ||
| 44 | companion object { | 44 | companion object { |
| 45 | val extensions: Set<String> = HashSet( | 45 | val extensions: Set<String> = HashSet( |
| 46 | listOf(".xci", ".nsp", ".nca", ".nro") | 46 | listOf("xci", "nsp", "nca", "nro") |
| 47 | ) | 47 | ) |
| 48 | } | 48 | } |
| 49 | } | 49 | } |
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 3086cfad3..f7d7aed1e 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 | |||
| @@ -296,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 296 | return@registerForActivityResult | 296 | return@registerForActivityResult |
| 297 | } | 297 | } |
| 298 | 298 | ||
| 299 | if (!FileUtil.hasExtension(result, "keys")) { | 299 | if (FileUtil.getExtension(result) != "keys") { |
| 300 | MessageDialogFragment.newInstance( | 300 | MessageDialogFragment.newInstance( |
| 301 | R.string.reading_keys_failure, | 301 | R.string.reading_keys_failure, |
| 302 | R.string.install_prod_keys_failure_extension_description | 302 | R.string.install_prod_keys_failure_extension_description |
| @@ -393,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 393 | return@registerForActivityResult | 393 | return@registerForActivityResult |
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | if (!FileUtil.hasExtension(result, "bin")) { | 396 | if (FileUtil.getExtension(result) != "bin") { |
| 397 | MessageDialogFragment.newInstance( | 397 | MessageDialogFragment.newInstance( |
| 398 | R.string.reading_keys_failure, | 398 | R.string.reading_keys_failure, |
| 399 | R.string.install_amiibo_keys_failure_extension_description | 399 | R.string.install_amiibo_keys_failure_extension_description |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index 9f3bbe56f..142af5f26 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | |||
| @@ -7,7 +7,6 @@ import android.content.Context | |||
| 7 | import android.database.Cursor | 7 | import android.database.Cursor |
| 8 | import android.net.Uri | 8 | import android.net.Uri |
| 9 | import android.provider.DocumentsContract | 9 | import android.provider.DocumentsContract |
| 10 | import android.provider.OpenableColumns | ||
| 11 | import androidx.documentfile.provider.DocumentFile | 10 | import androidx.documentfile.provider.DocumentFile |
| 12 | import java.io.BufferedInputStream | 11 | import java.io.BufferedInputStream |
| 13 | import java.io.File | 12 | import java.io.File |
| @@ -185,19 +184,18 @@ object FileUtil { | |||
| 185 | 184 | ||
| 186 | /** | 185 | /** |
| 187 | * Get file display name from given path | 186 | * Get file display name from given path |
| 188 | * @param path content uri path | 187 | * @param uri content uri |
| 189 | * @return String display name | 188 | * @return String display name |
| 190 | */ | 189 | */ |
| 191 | fun getFilename(context: Context, path: String): String { | 190 | fun getFilename(uri: Uri): String { |
| 192 | val resolver = context.contentResolver | 191 | val resolver = YuzuApplication.appContext.contentResolver |
| 193 | val columns = arrayOf( | 192 | val columns = arrayOf( |
| 194 | DocumentsContract.Document.COLUMN_DISPLAY_NAME | 193 | DocumentsContract.Document.COLUMN_DISPLAY_NAME |
| 195 | ) | 194 | ) |
| 196 | var filename = "" | 195 | var filename = "" |
| 197 | var c: Cursor? = null | 196 | var c: Cursor? = null |
| 198 | try { | 197 | try { |
| 199 | val mUri = Uri.parse(path) | 198 | c = resolver.query(uri, columns, null, null, null) |
| 200 | c = resolver.query(mUri, columns, null, null, null) | ||
| 201 | c!!.moveToNext() | 199 | c!!.moveToNext() |
| 202 | filename = c.getString(0) | 200 | filename = c.getString(0) |
| 203 | } catch (e: Exception) { | 201 | } catch (e: Exception) { |
| @@ -326,25 +324,9 @@ object FileUtil { | |||
| 326 | } | 324 | } |
| 327 | } | 325 | } |
| 328 | 326 | ||
| 329 | fun hasExtension(path: String, extension: String): Boolean = | 327 | fun getExtension(uri: Uri): String { |
| 330 | path.substring(path.lastIndexOf(".") + 1).contains(extension) | 328 | val fileName = getFilename(uri) |
| 331 | 329 | return fileName.substring(fileName.lastIndexOf(".") + 1) | |
| 332 | fun hasExtension(uri: Uri, extension: String): Boolean { | 330 | .lowercase() |
| 333 | val fileName: String? | ||
| 334 | val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null) | ||
| 335 | val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME) | ||
| 336 | cursor?.moveToFirst() | ||
| 337 | |||
| 338 | if (nameIndex == null) { | ||
| 339 | return false | ||
| 340 | } | ||
| 341 | |||
| 342 | fileName = cursor.getString(nameIndex) | ||
| 343 | cursor.close() | ||
| 344 | |||
| 345 | if (fileName == null) { | ||
| 346 | return false | ||
| 347 | } | ||
| 348 | return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension) | ||
| 349 | } | 331 | } |
| 350 | } | 332 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index ee9f3e570..f8e7eeca7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt | |||
| @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils | |||
| 6 | import android.content.SharedPreferences | 6 | import android.content.SharedPreferences |
| 7 | import android.net.Uri | 7 | import android.net.Uri |
| 8 | import androidx.preference.PreferenceManager | 8 | import androidx.preference.PreferenceManager |
| 9 | import java.util.* | ||
| 10 | import kotlinx.serialization.encodeToString | 9 | import kotlinx.serialization.encodeToString |
| 11 | import kotlinx.serialization.json.Json | 10 | import kotlinx.serialization.json.Json |
| 12 | import org.yuzu.yuzu_emu.NativeLibrary | 11 | import org.yuzu.yuzu_emu.NativeLibrary |
| @@ -33,15 +32,9 @@ object GameHelper { | |||
| 33 | val children = FileUtil.listFiles(context, gamesUri) | 32 | val children = FileUtil.listFiles(context, gamesUri) |
| 34 | for (file in children) { | 33 | for (file in children) { |
| 35 | if (!file.isDirectory) { | 34 | if (!file.isDirectory) { |
| 36 | val filename = file.uri.toString() | 35 | // Check that the file has an extension we care about before trying to read out of it. |
| 37 | val extensionStart = filename.lastIndexOf('.') | 36 | if (Game.extensions.contains(FileUtil.getExtension(file.uri))) { |
| 38 | if (extensionStart > 0) { | 37 | games.add(getGame(file.uri)) |
| 39 | val fileExtension = filename.substring(extensionStart) | ||
| 40 | |||
| 41 | // Check that the file has an extension we care about before trying to read out of it. | ||
| 42 | if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) { | ||
| 43 | games.add(getGame(filename)) | ||
| 44 | } | ||
| 45 | } | 38 | } |
| 46 | } | 39 | } |
| 47 | } | 40 | } |
| @@ -59,21 +52,19 @@ object GameHelper { | |||
| 59 | return games.toList() | 52 | return games.toList() |
| 60 | } | 53 | } |
| 61 | 54 | ||
| 62 | private fun getGame(filePath: String): Game { | 55 | private fun getGame(uri: Uri): Game { |
| 56 | val filePath = uri.toString() | ||
| 63 | var name = NativeLibrary.getTitle(filePath) | 57 | var name = NativeLibrary.getTitle(filePath) |
| 64 | 58 | ||
| 65 | // If the game's title field is empty, use the filename. | 59 | // If the game's title field is empty, use the filename. |
| 66 | if (name.isEmpty()) { | 60 | if (name.isEmpty()) { |
| 67 | name = filePath.substring(filePath.lastIndexOf("/") + 1) | 61 | name = FileUtil.getFilename(uri) |
| 68 | } | 62 | } |
| 69 | var gameId = NativeLibrary.getGameId(filePath) | 63 | var gameId = NativeLibrary.getGameId(filePath) |
| 70 | 64 | ||
| 71 | // If the game's ID field is empty, use the filename without extension. | 65 | // If the game's ID field is empty, use the filename without extension. |
| 72 | if (gameId.isEmpty()) { | 66 | if (gameId.isEmpty()) { |
| 73 | gameId = filePath.substring( | 67 | gameId = name.substring(0, name.lastIndexOf(".")) |
| 74 | filePath.lastIndexOf("/") + 1, | ||
| 75 | filePath.lastIndexOf(".") | ||
| 76 | ) | ||
| 77 | } | 68 | } |
| 78 | 69 | ||
| 79 | val newGame = Game( | 70 | val newGame = Game( |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index d576aac50..8bc6a4a04 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -60,6 +60,9 @@ | |||
| 60 | #include "video_core/rasterizer_interface.h" | 60 | #include "video_core/rasterizer_interface.h" |
| 61 | #include "video_core/renderer_base.h" | 61 | #include "video_core/renderer_base.h" |
| 62 | 62 | ||
| 63 | #define jconst [[maybe_unused]] const auto | ||
| 64 | #define jauto [[maybe_unused]] auto | ||
| 65 | |||
| 63 | namespace { | 66 | namespace { |
| 64 | 67 | ||
| 65 | class EmulationSession final { | 68 | class EmulationSession final { |
| @@ -99,8 +102,8 @@ public: | |||
| 99 | } | 102 | } |
| 100 | 103 | ||
| 101 | int InstallFileToNand(std::string filename) { | 104 | int InstallFileToNand(std::string filename) { |
| 102 | const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, | 105 | jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, |
| 103 | std::size_t block_size) { | 106 | std::size_t block_size) { |
| 104 | if (src == nullptr || dest == nullptr) { | 107 | if (src == nullptr || dest == nullptr) { |
| 105 | return false; | 108 | return false; |
| 106 | } | 109 | } |
| @@ -109,10 +112,10 @@ public: | |||
| 109 | } | 112 | } |
| 110 | 113 | ||
| 111 | using namespace Common::Literals; | 114 | using namespace Common::Literals; |
| 112 | std::vector<u8> buffer(1_MiB); | 115 | [[maybe_unused]] std::vector<u8> buffer(1_MiB); |
| 113 | 116 | ||
| 114 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 117 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 115 | const auto read = src->Read(buffer.data(), buffer.size(), i); | 118 | jconst read = src->Read(buffer.data(), buffer.size(), i); |
| 116 | dest->Write(buffer.data(), read, i); | 119 | dest->Write(buffer.data(), read, i); |
| 117 | } | 120 | } |
| 118 | return true; | 121 | return true; |
| @@ -129,14 +132,14 @@ public: | |||
| 129 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 132 | m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 130 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | 133 | m_system.GetFileSystemController().CreateFactories(*m_vfs); |
| 131 | 134 | ||
| 132 | std::shared_ptr<FileSys::NSP> nsp; | 135 | [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp; |
| 133 | if (filename.ends_with("nsp")) { | 136 | if (filename.ends_with("nsp")) { |
| 134 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | 137 | nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| 135 | if (nsp->IsExtractedType()) { | 138 | if (nsp->IsExtractedType()) { |
| 136 | return InstallError; | 139 | return InstallError; |
| 137 | } | 140 | } |
| 138 | } else if (filename.ends_with("xci")) { | 141 | } else if (filename.ends_with("xci")) { |
| 139 | const auto xci = | 142 | jconst xci = |
| 140 | std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); | 143 | std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read)); |
| 141 | nsp = xci->GetSecurePartitionNSP(); | 144 | nsp = xci->GetSecurePartitionNSP(); |
| 142 | } else { | 145 | } else { |
| @@ -151,7 +154,7 @@ public: | |||
| 151 | return InstallError; | 154 | return InstallError; |
| 152 | } | 155 | } |
| 153 | 156 | ||
| 154 | const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( | 157 | jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry( |
| 155 | *nsp, true, copy_func); | 158 | *nsp, true, copy_func); |
| 156 | 159 | ||
| 157 | switch (res) { | 160 | switch (res) { |
| @@ -234,7 +237,7 @@ public: | |||
| 234 | m_system.SetFilesystem(m_vfs); | 237 | m_system.SetFilesystem(m_vfs); |
| 235 | 238 | ||
| 236 | // Initialize system. | 239 | // Initialize system. |
| 237 | auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); | 240 | jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); |
| 238 | m_software_keyboard = android_keyboard.get(); | 241 | m_software_keyboard = android_keyboard.get(); |
| 239 | m_system.SetShuttingDown(false); | 242 | m_system.SetShuttingDown(false); |
| 240 | m_system.ApplySettings(); | 243 | m_system.ApplySettings(); |
| @@ -332,7 +335,7 @@ public: | |||
| 332 | 335 | ||
| 333 | while (true) { | 336 | while (true) { |
| 334 | { | 337 | { |
| 335 | std::unique_lock lock(m_mutex); | 338 | [[maybe_unused]] std::unique_lock lock(m_mutex); |
| 336 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), | 339 | if (m_cv.wait_for(lock, std::chrono::milliseconds(800), |
| 337 | [&]() { return !m_is_running; })) { | 340 | [&]() { return !m_is_running; })) { |
| 338 | // Emulation halted. | 341 | // Emulation halted. |
| @@ -364,7 +367,7 @@ public: | |||
| 364 | } | 367 | } |
| 365 | 368 | ||
| 366 | bool IsHandheldOnly() { | 369 | bool IsHandheldOnly() { |
| 367 | const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); | 370 | jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag(); |
| 368 | 371 | ||
| 369 | if (npad_style_set.fullkey == 1) { | 372 | if (npad_style_set.fullkey == 1) { |
| 370 | return false; | 373 | return false; |
| @@ -377,17 +380,17 @@ public: | |||
| 377 | return !Settings::values.use_docked_mode.GetValue(); | 380 | return !Settings::values.use_docked_mode.GetValue(); |
| 378 | } | 381 | } |
| 379 | 382 | ||
| 380 | void SetDeviceType(int index, int type) { | 383 | void SetDeviceType([[maybe_unused]] int index, int type) { |
| 381 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 384 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 382 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); | 385 | controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type)); |
| 383 | } | 386 | } |
| 384 | 387 | ||
| 385 | void OnGamepadConnectEvent(int index) { | 388 | void OnGamepadConnectEvent([[maybe_unused]] int index) { |
| 386 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 389 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 387 | 390 | ||
| 388 | // Ensure that player1 is configured correctly and handheld disconnected | 391 | // Ensure that player1 is configured correctly and handheld disconnected |
| 389 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { | 392 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) { |
| 390 | auto handheld = | 393 | jauto handheld = |
| 391 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); | 394 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
| 392 | 395 | ||
| 393 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { | 396 | if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { |
| @@ -399,7 +402,8 @@ public: | |||
| 399 | 402 | ||
| 400 | // Ensure that handheld is configured correctly and player 1 disconnected | 403 | // Ensure that handheld is configured correctly and player 1 disconnected |
| 401 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { | 404 | if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) { |
| 402 | auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | 405 | jauto player1 = |
| 406 | m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||
| 403 | 407 | ||
| 404 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { | 408 | if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) { |
| 405 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); | 409 | player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); |
| @@ -413,8 +417,8 @@ public: | |||
| 413 | } | 417 | } |
| 414 | } | 418 | } |
| 415 | 419 | ||
| 416 | void OnGamepadDisconnectEvent(int index) { | 420 | void OnGamepadDisconnectEvent([[maybe_unused]] int index) { |
| 417 | auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); | 421 | jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index); |
| 418 | controller->Disconnect(); | 422 | controller->Disconnect(); |
| 419 | } | 423 | } |
| 420 | 424 | ||
| @@ -430,7 +434,7 @@ private: | |||
| 430 | }; | 434 | }; |
| 431 | 435 | ||
| 432 | RomMetadata GetRomMetadata(const std::string& path) { | 436 | RomMetadata GetRomMetadata(const std::string& path) { |
| 433 | if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { | 437 | if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) { |
| 434 | return search->second; | 438 | return search->second; |
| 435 | } | 439 | } |
| 436 | 440 | ||
| @@ -438,14 +442,14 @@ private: | |||
| 438 | } | 442 | } |
| 439 | 443 | ||
| 440 | RomMetadata CacheRomMetadata(const std::string& path) { | 444 | RomMetadata CacheRomMetadata(const std::string& path) { |
| 441 | const auto file = Core::GetGameFileFromPath(m_vfs, path); | 445 | jconst file = Core::GetGameFileFromPath(m_vfs, path); |
| 442 | auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); | 446 | jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0); |
| 443 | 447 | ||
| 444 | RomMetadata entry; | 448 | RomMetadata entry; |
| 445 | loader->ReadTitle(entry.title); | 449 | loader->ReadTitle(entry.title); |
| 446 | loader->ReadIcon(entry.icon); | 450 | loader->ReadIcon(entry.icon); |
| 447 | if (loader->GetFileType() == Loader::FileType::NRO) { | 451 | if (loader->GetFileType() == Loader::FileType::NRO) { |
| 448 | auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); | 452 | jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); |
| 449 | entry.isHomebrew = loader_nro->IsHomebrew(); | 453 | entry.isHomebrew = loader_nro->IsHomebrew(); |
| 450 | } else { | 454 | } else { |
| 451 | entry.isHomebrew = false; | 455 | entry.isHomebrew = false; |
| @@ -516,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 516 | 520 | ||
| 517 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); | 521 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); |
| 518 | 522 | ||
| 519 | const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath); | 523 | jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); |
| 520 | if (result != Core::SystemResultStatus::Success) { | 524 | if (result != Core::SystemResultStatus::Success) { |
| 521 | return result; | 525 | return result; |
| 522 | } | 526 | } |
| @@ -528,24 +532,25 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 528 | 532 | ||
| 529 | extern "C" { | 533 | extern "C" { |
| 530 | 534 | ||
| 531 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jclass clazz, jobject surf) { | 535 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance, |
| 536 | [[maybe_unused]] jobject surf) { | ||
| 532 | EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); | 537 | EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf)); |
| 533 | EmulationSession::GetInstance().SurfaceChanged(); | 538 | EmulationSession::GetInstance().SurfaceChanged(); |
| 534 | } | 539 | } |
| 535 | 540 | ||
| 536 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jclass clazz) { | 541 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) { |
| 537 | ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); | 542 | ANativeWindow_release(EmulationSession::GetInstance().NativeWindow()); |
| 538 | EmulationSession::GetInstance().SetNativeWindow(nullptr); | 543 | EmulationSession::GetInstance().SetNativeWindow(nullptr); |
| 539 | EmulationSession::GetInstance().SurfaceChanged(); | 544 | EmulationSession::GetInstance().SurfaceChanged(); |
| 540 | } | 545 | } |
| 541 | 546 | ||
| 542 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jclass clazz, | 547 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance, |
| 543 | jstring j_directory) { | 548 | [[maybe_unused]] jstring j_directory) { |
| 544 | Common::FS::SetAppDirectory(GetJString(env, j_directory)); | 549 | Common::FS::SetAppDirectory(GetJString(env, j_directory)); |
| 545 | } | 550 | } |
| 546 | 551 | ||
| 547 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jclass clazz, | 552 | int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, |
| 548 | jstring j_file) { | 553 | [[maybe_unused]] jstring j_file) { |
| 549 | return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); | 554 | return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file)); |
| 550 | } | 555 | } |
| 551 | 556 | ||
| @@ -570,7 +575,7 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e | |||
| 570 | } | 575 | } |
| 571 | 576 | ||
| 572 | jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading( | 577 | jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading( |
| 573 | JNIEnv* env, [[maybe_unused]] jobject instance) { | 578 | JNIEnv* env, jobject instance) { |
| 574 | #ifdef ARCHITECTURE_arm64 | 579 | #ifdef ARCHITECTURE_arm64 |
| 575 | // If the KGSL device exists custom drivers can be loaded using adrenotools | 580 | // If the KGSL device exists custom drivers can be loaded using adrenotools |
| 576 | return SupportsCustomDriver(); | 581 | return SupportsCustomDriver(); |
| @@ -648,8 +653,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* | |||
| 648 | return static_cast<jboolean>(true); | 653 | return static_cast<jboolean>(true); |
| 649 | } | 654 | } |
| 650 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz, | 655 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz, |
| 651 | [[maybe_unused]] jint j_device, | 656 | jint j_device, jint j_button, |
| 652 | jint j_button, jint action) { | 657 | jint action) { |
| 653 | if (EmulationSession::GetInstance().IsRunning()) { | 658 | if (EmulationSession::GetInstance().IsRunning()) { |
| 654 | // Ensure gamepad is connected | 659 | // Ensure gamepad is connected |
| 655 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); | 660 | EmulationSession::GetInstance().OnGamepadConnectEvent(j_device); |
| @@ -718,8 +723,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c | |||
| 718 | } | 723 | } |
| 719 | 724 | ||
| 720 | jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, | 725 | jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz, |
| 721 | [[maybe_unused]] jstring j_filename) { | 726 | jstring j_filename) { |
| 722 | auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); | 727 | jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename)); |
| 723 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); | 728 | jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size())); |
| 724 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), | 729 | env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), |
| 725 | reinterpret_cast<jbyte*>(icon_data.data())); | 730 | reinterpret_cast<jbyte*>(icon_data.data())); |
| @@ -727,8 +732,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass cla | |||
| 727 | } | 732 | } |
| 728 | 733 | ||
| 729 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, | 734 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz, |
| 730 | [[maybe_unused]] jstring j_filename) { | 735 | jstring j_filename) { |
| 731 | auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); | 736 | jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename)); |
| 732 | return env->NewStringUTF(title.c_str()); | 737 | return env->NewStringUTF(title.c_str()); |
| 733 | } | 738 | } |
| 734 | 739 | ||
| @@ -743,22 +748,21 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass claz | |||
| 743 | } | 748 | } |
| 744 | 749 | ||
| 745 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, | 750 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz, |
| 746 | [[maybe_unused]] jstring j_filename) { | 751 | jstring j_filename) { |
| 747 | return env->NewStringUTF(""); | 752 | return env->NewStringUTF(""); |
| 748 | } | 753 | } |
| 749 | 754 | ||
| 750 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, | 755 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz, |
| 751 | [[maybe_unused]] jstring j_filename) { | 756 | jstring j_filename) { |
| 752 | return env->NewStringUTF(""); | 757 | return env->NewStringUTF(""); |
| 753 | } | 758 | } |
| 754 | 759 | ||
| 755 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, | 760 | jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz, |
| 756 | [[maybe_unused]] jstring j_filename) { | 761 | jstring j_filename) { |
| 757 | return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); | 762 | return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename)); |
| 758 | } | 763 | } |
| 759 | 764 | ||
| 760 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation | 765 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) { |
| 761 | [[maybe_unused]] (JNIEnv* env, jclass clazz) { | ||
| 762 | // Create the default config.ini. | 766 | // Create the default config.ini. |
| 763 | Config{}; | 767 | Config{}; |
| 764 | // Initialize the emulated system. | 768 | // Initialize the emulated system. |
| @@ -770,8 +774,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl | |||
| 770 | } | 774 | } |
| 771 | 775 | ||
| 772 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( | 776 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( |
| 773 | JNIEnv* env, jclass clazz, [[maybe_unused]] jstring j_file, | 777 | JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} |
| 774 | [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} | ||
| 775 | 778 | ||
| 776 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { | 779 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { |
| 777 | Config{}; | 780 | Config{}; |
| @@ -816,7 +819,7 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl | |||
| 816 | jdoubleArray j_stats = env->NewDoubleArray(4); | 819 | jdoubleArray j_stats = env->NewDoubleArray(4); |
| 817 | 820 | ||
| 818 | if (EmulationSession::GetInstance().IsRunning()) { | 821 | if (EmulationSession::GetInstance().IsRunning()) { |
| 819 | const auto results = EmulationSession::GetInstance().PerfStats(); | 822 | jconst results = EmulationSession::GetInstance().PerfStats(); |
| 820 | 823 | ||
| 821 | // Converting the structure into an array makes it easier to pass it to the frontend | 824 | // Converting the structure into an array makes it easier to pass it to the frontend |
| 822 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, | 825 | double stats[4] = {results.system_fps, results.average_game_fps, results.frametime, |
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 1baf6d746..36e67c145 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp | |||
| @@ -605,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) { | |||
| 605 | } | 605 | } |
| 606 | 606 | ||
| 607 | u64 GetSize(const fs::path& path) { | 607 | u64 GetSize(const fs::path& path) { |
| 608 | #ifdef ANDROID | ||
| 609 | if (Android::IsContentUri(path)) { | ||
| 610 | return Android::GetSize(path); | ||
| 611 | } | ||
| 612 | #endif | ||
| 613 | |||
| 608 | std::error_code ec; | 614 | std::error_code ec; |
| 609 | 615 | ||
| 610 | const auto file_size = fs::file_size(path, ec); | 616 | const auto file_size = fs::file_size(path, ec); |
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index c937495f9..1ebc32c1e 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -1250,6 +1250,11 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( | |||
| 1250 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); | 1250 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); |
| 1251 | const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); | 1251 | const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode); |
| 1252 | 1252 | ||
| 1253 | // Restore previous state | ||
| 1254 | if (mapped_nfc_result != Common::Input::DriverResult::Success) { | ||
| 1255 | right_output_device->SetPollingMode(Common::Input::PollingMode::Active); | ||
| 1256 | } | ||
| 1257 | |||
| 1253 | if (virtual_nfc_result == Common::Input::DriverResult::Success) { | 1258 | if (virtual_nfc_result == Common::Input::DriverResult::Success) { |
| 1254 | return virtual_nfc_result; | 1259 | return virtual_nfc_result; |
| 1255 | } | 1260 | } |
| @@ -1329,16 +1334,22 @@ bool EmulatedController::StartNfcPolling() { | |||
| 1329 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1334 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1330 | auto& nfc_virtual_output_device = output_devices[3]; | 1335 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1331 | 1336 | ||
| 1332 | return nfc_output_device->StartNfcPolling() == Common::Input::NfcState::Success || | 1337 | const auto device_result = nfc_output_device->StartNfcPolling(); |
| 1333 | nfc_virtual_output_device->StartNfcPolling() == Common::Input::NfcState::Success; | 1338 | const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling(); |
| 1339 | |||
| 1340 | return device_result == Common::Input::NfcState::Success || | ||
| 1341 | virtual_device_result == Common::Input::NfcState::Success; | ||
| 1334 | } | 1342 | } |
| 1335 | 1343 | ||
| 1336 | bool EmulatedController::StopNfcPolling() { | 1344 | bool EmulatedController::StopNfcPolling() { |
| 1337 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1345 | auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 1338 | auto& nfc_virtual_output_device = output_devices[3]; | 1346 | auto& nfc_virtual_output_device = output_devices[3]; |
| 1339 | 1347 | ||
| 1340 | return nfc_output_device->StopNfcPolling() == Common::Input::NfcState::Success || | 1348 | const auto device_result = nfc_output_device->StopNfcPolling(); |
| 1341 | nfc_virtual_output_device->StopNfcPolling() == Common::Input::NfcState::Success; | 1349 | const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling(); |
| 1350 | |||
| 1351 | return device_result == Common::Input::NfcState::Success || | ||
| 1352 | virtual_device_result == Common::Input::NfcState::Success; | ||
| 1342 | } | 1353 | } |
| 1343 | 1354 | ||
| 1344 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { | 1355 | bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index ab64a64a2..9f26392b1 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -150,6 +150,8 @@ public: | |||
| 150 | if (sdl_controller) { | 150 | if (sdl_controller) { |
| 151 | const auto type = SDL_GameControllerGetType(sdl_controller.get()); | 151 | const auto type = SDL_GameControllerGetType(sdl_controller.get()); |
| 152 | return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || | 152 | return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || |
| 153 | (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) || | ||
| 154 | (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) || | ||
| 153 | (type == SDL_CONTROLLER_TYPE_PS5); | 155 | (type == SDL_CONTROLLER_TYPE_PS5); |
| 154 | } | 156 | } |
| 155 | return false; | 157 | return false; |
| @@ -228,9 +230,8 @@ public: | |||
| 228 | return false; | 230 | return false; |
| 229 | } | 231 | } |
| 230 | 232 | ||
| 231 | Common::Input::BatteryLevel GetBatteryLevel() { | 233 | Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) { |
| 232 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); | 234 | switch (battery_level) { |
| 233 | switch (level) { | ||
| 234 | case SDL_JOYSTICK_POWER_EMPTY: | 235 | case SDL_JOYSTICK_POWER_EMPTY: |
| 235 | return Common::Input::BatteryLevel::Empty; | 236 | return Common::Input::BatteryLevel::Empty; |
| 236 | case SDL_JOYSTICK_POWER_LOW: | 237 | case SDL_JOYSTICK_POWER_LOW: |
| @@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 378 | if (joystick_map.find(guid) == joystick_map.end()) { | 379 | if (joystick_map.find(guid) == joystick_map.end()) { |
| 379 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); | 380 | auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); |
| 380 | PreSetController(joystick->GetPadIdentifier()); | 381 | PreSetController(joystick->GetPadIdentifier()); |
| 381 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | ||
| 382 | joystick->EnableMotion(); | 382 | joystick->EnableMotion(); |
| 383 | joystick_map[guid].emplace_back(std::move(joystick)); | 383 | joystick_map[guid].emplace_back(std::move(joystick)); |
| 384 | return; | 384 | return; |
| @@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) { | |||
| 398 | const int port = static_cast<int>(joystick_guid_list.size()); | 398 | const int port = static_cast<int>(joystick_guid_list.size()); |
| 399 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); | 399 | auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); |
| 400 | PreSetController(joystick->GetPadIdentifier()); | 400 | PreSetController(joystick->GetPadIdentifier()); |
| 401 | SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); | ||
| 402 | joystick->EnableMotion(); | 401 | joystick->EnableMotion(); |
| 403 | joystick_guid_list.emplace_back(std::move(joystick)); | 402 | joystick_guid_list.emplace_back(std::move(joystick)); |
| 404 | } | 403 | } |
| @@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { | |||
| 438 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | 437 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { |
| 439 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | 438 | const PadIdentifier identifier = joystick->GetPadIdentifier(); |
| 440 | SetButton(identifier, event.jbutton.button, true); | 439 | SetButton(identifier, event.jbutton.button, true); |
| 441 | // Battery doesn't trigger an event so just update every button press | ||
| 442 | SetBattery(identifier, joystick->GetBatteryLevel()); | ||
| 443 | } | 440 | } |
| 444 | break; | 441 | break; |
| 445 | } | 442 | } |
| @@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { | |||
| 466 | } | 463 | } |
| 467 | break; | 464 | break; |
| 468 | } | 465 | } |
| 466 | case SDL_JOYBATTERYUPDATED: { | ||
| 467 | if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) { | ||
| 468 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | ||
| 469 | SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level)); | ||
| 470 | } | ||
| 471 | break; | ||
| 472 | } | ||
| 469 | case SDL_JOYDEVICEREMOVED: | 473 | case SDL_JOYDEVICEREMOVED: |
| 470 | LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); | 474 | LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which); |
| 471 | CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); | 475 | CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which)); |
| @@ -505,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en | |||
| 505 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); | 509 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); |
| 506 | } else { | 510 | } else { |
| 507 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); | 511 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); |
| 512 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0"); | ||
| 513 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0"); | ||
| 514 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1"); | ||
| 508 | } | 515 | } |
| 509 | 516 | ||
| 510 | // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled | 517 | // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled |
| @@ -512,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en | |||
| 512 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); | 519 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); |
| 513 | } else { | 520 | } else { |
| 514 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); | 521 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); |
| 522 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); | ||
| 515 | } | 523 | } |
| 516 | 524 | ||
| 525 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); | ||
| 526 | |||
| 517 | // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native | 527 | // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native |
| 518 | // driver on Linux. | 528 | // driver on Linux. |
| 519 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); | 529 | SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0"); |
| @@ -793,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p | |||
| 793 | // This list also excludes Screenshot since there's not really a mapping for that | 803 | // This list also excludes Screenshot since there's not really a mapping for that |
| 794 | ButtonBindings switch_to_sdl_button; | 804 | ButtonBindings switch_to_sdl_button; |
| 795 | 805 | ||
| 796 | if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { | 806 | if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || |
| 807 | SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || | ||
| 808 | SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) { | ||
| 797 | switch_to_sdl_button = GetNintendoButtonBinding(joystick); | 809 | switch_to_sdl_button = GetNintendoButtonBinding(joystick); |
| 798 | } else { | 810 | } else { |
| 799 | switch_to_sdl_button = GetDefaultButtonBinding(); | 811 | switch_to_sdl_button = GetDefaultButtonBinding(); |
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 2c8c66951..ec984a647 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp | |||
| @@ -72,6 +72,7 @@ DriverResult JoyconDriver::InitializeDevice() { | |||
| 72 | nfc_enabled = false; | 72 | nfc_enabled = false; |
| 73 | passive_enabled = false; | 73 | passive_enabled = false; |
| 74 | irs_enabled = false; | 74 | irs_enabled = false; |
| 75 | input_only_device = false; | ||
| 75 | gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; | 76 | gyro_sensitivity = Joycon::GyroSensitivity::DPS2000; |
| 76 | gyro_performance = Joycon::GyroPerformance::HZ833; | 77 | gyro_performance = Joycon::GyroPerformance::HZ833; |
| 77 | accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; | 78 | accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8; |
| @@ -86,16 +87,23 @@ DriverResult JoyconDriver::InitializeDevice() { | |||
| 86 | rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); | 87 | rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); |
| 87 | 88 | ||
| 88 | // Get fixed joycon info | 89 | // Get fixed joycon info |
| 89 | generic_protocol->GetVersionNumber(version); | 90 | if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) { |
| 90 | generic_protocol->SetLowPowerMode(false); | 91 | // If this command fails the device doesn't accept configuration commands |
| 91 | generic_protocol->GetColor(color); | 92 | input_only_device = true; |
| 92 | if (handle_device_type == ControllerType::Pro) { | ||
| 93 | // Some 3rd party controllers aren't pro controllers | ||
| 94 | generic_protocol->GetControllerType(device_type); | ||
| 95 | } else { | ||
| 96 | device_type = handle_device_type; | ||
| 97 | } | 93 | } |
| 98 | generic_protocol->GetSerialNumber(serial_number); | 94 | |
| 95 | if (!input_only_device) { | ||
| 96 | generic_protocol->SetLowPowerMode(false); | ||
| 97 | generic_protocol->GetColor(color); | ||
| 98 | if (handle_device_type == ControllerType::Pro) { | ||
| 99 | // Some 3rd party controllers aren't pro controllers | ||
| 100 | generic_protocol->GetControllerType(device_type); | ||
| 101 | } else { | ||
| 102 | device_type = handle_device_type; | ||
| 103 | } | ||
| 104 | generic_protocol->GetSerialNumber(serial_number); | ||
| 105 | } | ||
| 106 | |||
| 99 | supported_features = GetSupportedFeatures(); | 107 | supported_features = GetSupportedFeatures(); |
| 100 | 108 | ||
| 101 | // Get Calibration data | 109 | // Get Calibration data |
| @@ -261,6 +269,10 @@ DriverResult JoyconDriver::SetPollingMode() { | |||
| 261 | generic_protocol->EnableImu(false); | 269 | generic_protocol->EnableImu(false); |
| 262 | } | 270 | } |
| 263 | 271 | ||
| 272 | if (input_only_device) { | ||
| 273 | return DriverResult::NotSupported; | ||
| 274 | } | ||
| 275 | |||
| 264 | if (irs_protocol->IsEnabled()) { | 276 | if (irs_protocol->IsEnabled()) { |
| 265 | irs_protocol->DisableIrs(); | 277 | irs_protocol->DisableIrs(); |
| 266 | } | 278 | } |
| @@ -282,6 +294,7 @@ DriverResult JoyconDriver::SetPollingMode() { | |||
| 282 | } | 294 | } |
| 283 | irs_protocol->DisableIrs(); | 295 | irs_protocol->DisableIrs(); |
| 284 | LOG_ERROR(Input, "Error enabling IRS"); | 296 | LOG_ERROR(Input, "Error enabling IRS"); |
| 297 | return result; | ||
| 285 | } | 298 | } |
| 286 | 299 | ||
| 287 | if (nfc_enabled && supported_features.nfc) { | 300 | if (nfc_enabled && supported_features.nfc) { |
| @@ -291,6 +304,7 @@ DriverResult JoyconDriver::SetPollingMode() { | |||
| 291 | } | 304 | } |
| 292 | nfc_protocol->DisableNfc(); | 305 | nfc_protocol->DisableNfc(); |
| 293 | LOG_ERROR(Input, "Error enabling NFC"); | 306 | LOG_ERROR(Input, "Error enabling NFC"); |
| 307 | return result; | ||
| 294 | } | 308 | } |
| 295 | 309 | ||
| 296 | if (hidbus_enabled && supported_features.hidbus) { | 310 | if (hidbus_enabled && supported_features.hidbus) { |
| @@ -305,6 +319,7 @@ DriverResult JoyconDriver::SetPollingMode() { | |||
| 305 | ring_connected = false; | 319 | ring_connected = false; |
| 306 | ring_protocol->DisableRingCon(); | 320 | ring_protocol->DisableRingCon(); |
| 307 | LOG_ERROR(Input, "Error enabling Ringcon"); | 321 | LOG_ERROR(Input, "Error enabling Ringcon"); |
| 322 | return result; | ||
| 308 | } | 323 | } |
| 309 | 324 | ||
| 310 | if (passive_enabled && supported_features.passive) { | 325 | if (passive_enabled && supported_features.passive) { |
| @@ -333,6 +348,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() { | |||
| 333 | .vibration = true, | 348 | .vibration = true, |
| 334 | }; | 349 | }; |
| 335 | 350 | ||
| 351 | if (input_only_device) { | ||
| 352 | return features; | ||
| 353 | } | ||
| 354 | |||
| 336 | if (device_type == ControllerType::Right) { | 355 | if (device_type == ControllerType::Right) { |
| 337 | features.nfc = true; | 356 | features.nfc = true; |
| 338 | features.irs = true; | 357 | features.irs = true; |
| @@ -517,6 +536,11 @@ DriverResult JoyconDriver::StopNfcPolling() { | |||
| 517 | const auto result = nfc_protocol->StopNFCPollingMode(); | 536 | const auto result = nfc_protocol->StopNFCPollingMode(); |
| 518 | disable_input_thread = false; | 537 | disable_input_thread = false; |
| 519 | 538 | ||
| 539 | if (amiibo_detected) { | ||
| 540 | amiibo_detected = false; | ||
| 541 | joycon_poller->UpdateAmiibo({}); | ||
| 542 | } | ||
| 543 | |||
| 520 | return result; | 544 | return result; |
| 521 | } | 545 | } |
| 522 | 546 | ||
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index bc7025a21..45b32d2f8 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h | |||
| @@ -120,6 +120,7 @@ private: | |||
| 120 | // Hardware configuration | 120 | // Hardware configuration |
| 121 | u8 leds{}; | 121 | u8 leds{}; |
| 122 | ReportMode mode{}; | 122 | ReportMode mode{}; |
| 123 | bool input_only_device{}; | ||
| 123 | bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time | 124 | bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time |
| 124 | bool hidbus_enabled{}; // External device support | 125 | bool hidbus_enabled{}; // External device support |
| 125 | bool irs_enabled{}; // Infrared camera input | 126 | bool irs_enabled{}; // Infrared camera input |
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index 51669261a..88f4cec1c 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp | |||
| @@ -73,7 +73,7 @@ DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) { | |||
| 73 | DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, | 73 | DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, |
| 74 | SubCommandResponse& output) { | 74 | SubCommandResponse& output) { |
| 75 | constexpr int timeout_mili = 66; | 75 | constexpr int timeout_mili = 66; |
| 76 | constexpr int MaxTries = 15; | 76 | constexpr int MaxTries = 3; |
| 77 | int tries = 0; | 77 | int tries = 0; |
| 78 | 78 | ||
| 79 | do { | 79 | do { |
| @@ -113,9 +113,7 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const | |||
| 113 | return result; | 113 | return result; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | result = GetSubCommandResponse(sc, output); | 116 | return GetSubCommandResponse(sc, output); |
| 117 | |||
| 118 | return DriverResult::Success; | ||
| 119 | } | 117 | } |
| 120 | 118 | ||
| 121 | DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { | 119 | DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { |
| @@ -158,7 +156,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe | |||
| 158 | 156 | ||
| 159 | DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { | 157 | DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) { |
| 160 | constexpr std::size_t HeaderSize = 5; | 158 | constexpr std::size_t HeaderSize = 5; |
| 161 | constexpr std::size_t MaxTries = 10; | 159 | constexpr std::size_t MaxTries = 5; |
| 162 | std::size_t tries = 0; | 160 | std::size_t tries = 0; |
| 163 | SubCommandResponse response{}; | 161 | SubCommandResponse response{}; |
| 164 | std::array<u8, sizeof(ReadSpiPacket)> buffer{}; | 162 | std::array<u8, sizeof(ReadSpiPacket)> buffer{}; |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index fd4a61a4d..b795c0179 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp | |||
| @@ -461,7 +461,7 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I | |||
| 461 | header += fmt::format("R{},", index); | 461 | header += fmt::format("R{},", index); |
| 462 | } | 462 | } |
| 463 | if (program.local_memory_size > 0) { | 463 | if (program.local_memory_size > 0) { |
| 464 | header += fmt::format("lmem[{}],", program.local_memory_size); | 464 | header += fmt::format("lmem[{}],", Common::DivCeil(program.local_memory_size, 4U)); |
| 465 | } | 465 | } |
| 466 | if (program.info.uses_fswzadd) { | 466 | if (program.info.uses_fswzadd) { |
| 467 | header += "FSWZA[4],FSWZB[4],"; | 467 | header += "FSWZA[4],FSWZB[4],"; |
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 5a4195217..70292686f 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp | |||
| @@ -424,6 +424,10 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 424 | info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2; | 424 | info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2; |
| 425 | info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4; | 425 | info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4; |
| 426 | break; | 426 | break; |
| 427 | case IR::Opcode::LoadLocal: | ||
| 428 | case IR::Opcode::WriteLocal: | ||
| 429 | info.uses_local_memory = true; | ||
| 430 | break; | ||
| 427 | default: | 431 | default: |
| 428 | break; | 432 | break; |
| 429 | } | 433 | } |
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index d308db942..b4b4afd37 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h | |||
| @@ -172,6 +172,7 @@ struct Info { | |||
| 172 | bool stores_indexed_attributes{}; | 172 | bool stores_indexed_attributes{}; |
| 173 | 173 | ||
| 174 | bool stores_global_memory{}; | 174 | bool stores_global_memory{}; |
| 175 | bool uses_local_memory{}; | ||
| 175 | 176 | ||
| 176 | bool uses_fp16{}; | 177 | bool uses_fp16{}; |
| 177 | bool uses_fp64{}; | 178 | bool uses_fp64{}; |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 2442c3c29..e61d9af80 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -33,6 +33,7 @@ set(SHADER_FILES | |||
| 33 | opengl_fidelityfx_fsr.frag | 33 | opengl_fidelityfx_fsr.frag |
| 34 | opengl_fidelityfx_fsr_easu.frag | 34 | opengl_fidelityfx_fsr_easu.frag |
| 35 | opengl_fidelityfx_fsr_rcas.frag | 35 | opengl_fidelityfx_fsr_rcas.frag |
| 36 | opengl_lmem_warmup.comp | ||
| 36 | opengl_present.frag | 37 | opengl_present.frag |
| 37 | opengl_present.vert | 38 | opengl_present.vert |
| 38 | opengl_present_scaleforce.frag | 39 | opengl_present_scaleforce.frag |
diff --git a/src/video_core/host_shaders/opengl_lmem_warmup.comp b/src/video_core/host_shaders/opengl_lmem_warmup.comp new file mode 100644 index 000000000..518268477 --- /dev/null +++ b/src/video_core/host_shaders/opengl_lmem_warmup.comp | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | // This shader is a workaround for a quirk in NVIDIA OpenGL drivers | ||
| 5 | // Shaders using local memory see a great performance benefit if a shader that was dispatched | ||
| 6 | // before it had more local memory allocated. | ||
| 7 | // This shader allocates the maximum local memory allowed on NVIDIA drivers to ensure that | ||
| 8 | // subsequent shaders see the performance boost. | ||
| 9 | |||
| 10 | // NOTE: This shader does no actual meaningful work and returns immediately, | ||
| 11 | // it is simply a means to have the driver expect a shader using lots of local memory. | ||
| 12 | |||
| 13 | #version 450 | ||
| 14 | |||
| 15 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; | ||
| 16 | |||
| 17 | layout(location = 0) uniform uint uniform_data; | ||
| 18 | |||
| 19 | layout(binding = 0, rgba8) uniform writeonly restrict image2DArray dest_image; | ||
| 20 | |||
| 21 | #define MAX_LMEM_SIZE 4080 // Size chosen to avoid errors in Nvidia's GLSL compiler | ||
| 22 | #define NUM_LMEM_CONSTANTS 1 | ||
| 23 | #define ARRAY_SIZE MAX_LMEM_SIZE - NUM_LMEM_CONSTANTS | ||
| 24 | |||
| 25 | uint lmem_0[ARRAY_SIZE]; | ||
| 26 | const uvec4 constant_values[NUM_LMEM_CONSTANTS] = uvec4[](uvec4(0)); | ||
| 27 | |||
| 28 | void main() { | ||
| 29 | const uint global_id = gl_GlobalInvocationID.x; | ||
| 30 | if (global_id <= 128) { | ||
| 31 | // Since the shader is called with a dispatch of 1x1x1 | ||
| 32 | // This should always be the case, and this shader will not actually execute | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | for (uint t = 0; t < uniform_data; t++) { | ||
| 36 | const uint offset = (t * uniform_data); | ||
| 37 | lmem_0[offset] = t; | ||
| 38 | } | ||
| 39 | const uint offset = (gl_GlobalInvocationID.y * uniform_data + gl_GlobalInvocationID.x); | ||
| 40 | const uint value = lmem_0[offset]; | ||
| 41 | const uint const_value = constant_values[offset / 4][offset % 4]; | ||
| 42 | const uvec4 color = uvec4(value + const_value); | ||
| 43 | |||
| 44 | // A "side-effect" is needed so the variables don't get optimized out, | ||
| 45 | // but this should never execute so there should be no clobbering of previously bound state. | ||
| 46 | imageStore(dest_image, ivec3(gl_GlobalInvocationID), color); | ||
| 47 | } | ||
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 3151c0db8..f9ca55c36 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp | |||
| @@ -63,6 +63,7 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac | |||
| 63 | writes_global_memory = !use_storage_buffers && | 63 | writes_global_memory = !use_storage_buffers && |
| 64 | std::ranges::any_of(info.storage_buffers_descriptors, | 64 | std::ranges::any_of(info.storage_buffers_descriptors, |
| 65 | [](const auto& desc) { return desc.is_written; }); | 65 | [](const auto& desc) { return desc.is_written; }); |
| 66 | uses_local_memory = info.uses_local_memory; | ||
| 66 | if (force_context_flush) { | 67 | if (force_context_flush) { |
| 67 | std::scoped_lock lock{built_mutex}; | 68 | std::scoped_lock lock{built_mutex}; |
| 68 | built_fence.Create(); | 69 | built_fence.Create(); |
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h index 9bcc72b59..c26b4fa5e 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.h +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h | |||
| @@ -59,6 +59,10 @@ public: | |||
| 59 | return writes_global_memory; | 59 | return writes_global_memory; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | [[nodiscard]] bool UsesLocalMemory() const noexcept { | ||
| 63 | return uses_local_memory; | ||
| 64 | } | ||
| 65 | |||
| 62 | void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, | 66 | void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_, |
| 63 | Tegra::MemoryManager* gpu_memory_) { | 67 | Tegra::MemoryManager* gpu_memory_) { |
| 64 | kepler_compute = kepler_compute_; | 68 | kepler_compute = kepler_compute_; |
| @@ -84,6 +88,7 @@ private: | |||
| 84 | 88 | ||
| 85 | bool use_storage_buffers{}; | 89 | bool use_storage_buffers{}; |
| 86 | bool writes_global_memory{}; | 90 | bool writes_global_memory{}; |
| 91 | bool uses_local_memory{}; | ||
| 87 | 92 | ||
| 88 | std::mutex built_mutex; | 93 | std::mutex built_mutex; |
| 89 | std::condition_variable built_condvar; | 94 | std::condition_variable built_condvar; |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 03d234f2f..33e63c17d 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -194,6 +194,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) { | |||
| 194 | has_bool_ref_bug = true; | 194 | has_bool_ref_bug = true; |
| 195 | } | 195 | } |
| 196 | } | 196 | } |
| 197 | has_lmem_perf_bug = is_nvidia; | ||
| 197 | 198 | ||
| 198 | strict_context_required = emu_window.StrictContextRequired(); | 199 | strict_context_required = emu_window.StrictContextRequired(); |
| 199 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. | 200 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index ad27264e5..a5a6bbbba 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -192,6 +192,10 @@ public: | |||
| 192 | return supports_conditional_barriers; | 192 | return supports_conditional_barriers; |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | bool HasLmemPerfBug() const { | ||
| 196 | return has_lmem_perf_bug; | ||
| 197 | } | ||
| 198 | |||
| 195 | private: | 199 | private: |
| 196 | static bool TestVariableAoffi(); | 200 | static bool TestVariableAoffi(); |
| 197 | static bool TestPreciseBug(); | 201 | static bool TestPreciseBug(); |
| @@ -238,6 +242,7 @@ private: | |||
| 238 | bool can_report_memory{}; | 242 | bool can_report_memory{}; |
| 239 | bool strict_context_required{}; | 243 | bool strict_context_required{}; |
| 240 | bool supports_conditional_barriers{}; | 244 | bool supports_conditional_barriers{}; |
| 245 | bool has_lmem_perf_bug{}; | ||
| 241 | 246 | ||
| 242 | std::string vendor_name; | 247 | std::string vendor_name; |
| 243 | }; | 248 | }; |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index c58f760b8..23a48c6fe 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp | |||
| @@ -215,6 +215,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c | |||
| 215 | 215 | ||
| 216 | writes_global_memory |= std::ranges::any_of( | 216 | writes_global_memory |= std::ranges::any_of( |
| 217 | info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); | 217 | info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; }); |
| 218 | uses_local_memory |= info.uses_local_memory; | ||
| 218 | } | 219 | } |
| 219 | ASSERT(num_textures <= MAX_TEXTURES); | 220 | ASSERT(num_textures <= MAX_TEXTURES); |
| 220 | ASSERT(num_images <= MAX_IMAGES); | 221 | ASSERT(num_images <= MAX_IMAGES); |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h index 7bab3be0a..7b3d7eae8 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h | |||
| @@ -98,6 +98,10 @@ public: | |||
| 98 | return writes_global_memory; | 98 | return writes_global_memory; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | [[nodiscard]] bool UsesLocalMemory() const noexcept { | ||
| 102 | return uses_local_memory; | ||
| 103 | } | ||
| 104 | |||
| 101 | [[nodiscard]] bool IsBuilt() noexcept; | 105 | [[nodiscard]] bool IsBuilt() noexcept; |
| 102 | 106 | ||
| 103 | template <typename Spec> | 107 | template <typename Spec> |
| @@ -146,6 +150,7 @@ private: | |||
| 146 | 150 | ||
| 147 | bool use_storage_buffers{}; | 151 | bool use_storage_buffers{}; |
| 148 | bool writes_global_memory{}; | 152 | bool writes_global_memory{}; |
| 153 | bool uses_local_memory{}; | ||
| 149 | 154 | ||
| 150 | static constexpr std::size_t XFB_ENTRY_STRIDE = 3; | 155 | static constexpr std::size_t XFB_ENTRY_STRIDE = 3; |
| 151 | GLsizei num_xfb_attribs{}; | 156 | GLsizei num_xfb_attribs{}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index fc711c44a..edf527f2d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -222,6 +222,9 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { | |||
| 222 | gpu.TickWork(); | 222 | gpu.TickWork(); |
| 223 | 223 | ||
| 224 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; | 224 | std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; |
| 225 | if (pipeline->UsesLocalMemory()) { | ||
| 226 | program_manager.LocalMemoryWarmup(); | ||
| 227 | } | ||
| 225 | pipeline->SetEngine(maxwell3d, gpu_memory); | 228 | pipeline->SetEngine(maxwell3d, gpu_memory); |
| 226 | pipeline->Configure(is_indexed); | 229 | pipeline->Configure(is_indexed); |
| 227 | 230 | ||
| @@ -371,6 +374,9 @@ void RasterizerOpenGL::DispatchCompute() { | |||
| 371 | if (!pipeline) { | 374 | if (!pipeline) { |
| 372 | return; | 375 | return; |
| 373 | } | 376 | } |
| 377 | if (pipeline->UsesLocalMemory()) { | ||
| 378 | program_manager.LocalMemoryWarmup(); | ||
| 379 | } | ||
| 374 | pipeline->SetEngine(kepler_compute, gpu_memory); | 380 | pipeline->SetEngine(kepler_compute, gpu_memory); |
| 375 | pipeline->Configure(); | 381 | pipeline->Configure(); |
| 376 | const auto& qmd{kepler_compute->launch_description}; | 382 | const auto& qmd{kepler_compute->launch_description}; |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 98841ae65..03d4b9d06 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #include <glad/glad.h> | 4 | #include <glad/glad.h> |
| 5 | 5 | ||
| 6 | #include "video_core/host_shaders/opengl_lmem_warmup_comp.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 7 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 8 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 7 | 9 | ||
| 8 | namespace OpenGL { | 10 | namespace OpenGL { |
| 9 | 11 | ||
| @@ -17,6 +19,10 @@ ProgramManager::ProgramManager(const Device& device) { | |||
| 17 | if (device.UseAssemblyShaders()) { | 19 | if (device.UseAssemblyShaders()) { |
| 18 | glEnable(GL_COMPUTE_PROGRAM_NV); | 20 | glEnable(GL_COMPUTE_PROGRAM_NV); |
| 19 | } | 21 | } |
| 22 | if (device.HasLmemPerfBug()) { | ||
| 23 | lmem_warmup_program = | ||
| 24 | CreateProgram(HostShaders::OPENGL_LMEM_WARMUP_COMP, GL_COMPUTE_SHADER); | ||
| 25 | } | ||
| 20 | } | 26 | } |
| 21 | 27 | ||
| 22 | void ProgramManager::BindComputeProgram(GLuint program) { | 28 | void ProgramManager::BindComputeProgram(GLuint program) { |
| @@ -98,6 +104,13 @@ void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NU | |||
| 98 | 104 | ||
| 99 | void ProgramManager::RestoreGuestCompute() {} | 105 | void ProgramManager::RestoreGuestCompute() {} |
| 100 | 106 | ||
| 107 | void ProgramManager::LocalMemoryWarmup() { | ||
| 108 | if (lmem_warmup_program.handle != 0) { | ||
| 109 | BindComputeProgram(lmem_warmup_program.handle); | ||
| 110 | glDispatchCompute(1, 1, 1); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 101 | void ProgramManager::BindPipeline() { | 114 | void ProgramManager::BindPipeline() { |
| 102 | if (!is_pipeline_bound) { | 115 | if (!is_pipeline_bound) { |
| 103 | is_pipeline_bound = true; | 116 | is_pipeline_bound = true; |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 07ffab77f..852d8c88e 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -30,6 +30,8 @@ public: | |||
| 30 | 30 | ||
| 31 | void RestoreGuestCompute(); | 31 | void RestoreGuestCompute(); |
| 32 | 32 | ||
| 33 | void LocalMemoryWarmup(); | ||
| 34 | |||
| 33 | private: | 35 | private: |
| 34 | void BindPipeline(); | 36 | void BindPipeline(); |
| 35 | 37 | ||
| @@ -44,6 +46,7 @@ private: | |||
| 44 | u32 current_stage_mask = 0; | 46 | u32 current_stage_mask = 0; |
| 45 | std::array<GLuint, NUM_STAGES> current_programs{}; | 47 | std::array<GLuint, NUM_STAGES> current_programs{}; |
| 46 | GLuint current_assembly_compute_program = 0; | 48 | GLuint current_assembly_compute_program = 0; |
| 49 | OGLProgram lmem_warmup_program; | ||
| 47 | }; | 50 | }; |
| 48 | 51 | ||
| 49 | } // namespace OpenGL | 52 | } // namespace OpenGL |
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui index 514dff372..38ecccc3d 100644 --- a/src/yuzu/configuration/configure_ringcon.ui +++ b/src/yuzu/configuration/configure_ringcon.ui | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | </size> | 23 | </size> |
| 24 | </property> | 24 | </property> |
| 25 | <property name="text"> | 25 | <property name="text"> |
| 26 | <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string> | 26 | <string>To use Ring-Con, configure player 1 as right Joy-Con (both physical and emulated), and player 2 as left Joy-Con (left physical and dual emulated) before starting the game.</string> |
| 27 | </property> | 27 | </property> |
| 28 | <property name="wordWrap"> | 28 | <property name="wordWrap"> |
| 29 | <bool>true</bool> | 29 | <bool>true</bool> |