diff options
55 files changed, 748 insertions, 369 deletions
diff --git a/.gitmodules b/.gitmodules index 95eae8109..89f2ad924 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -52,3 +52,6 @@ | |||
| 52 | [submodule "libadrenotools"] | 52 | [submodule "libadrenotools"] |
| 53 | path = externals/libadrenotools | 53 | path = externals/libadrenotools |
| 54 | url = https://github.com/bylaws/libadrenotools | 54 | url = https://github.com/bylaws/libadrenotools |
| 55 | [submodule "tzdb_to_nx"] | ||
| 56 | path = externals/nx_tzdb/tzdb_to_nx | ||
| 57 | url = https://github.com/lat9nq/tzdb_to_nx.git | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d03bbf94..6d3146c9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -59,6 +59,8 @@ option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON) | |||
| 59 | 59 | ||
| 60 | option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) | 60 | option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) |
| 61 | 61 | ||
| 62 | option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF) | ||
| 63 | |||
| 62 | CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) | 64 | CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) |
| 63 | 65 | ||
| 64 | # On Android, fetch and compile libcxx before doing anything else | 66 | # On Android, fetch and compile libcxx before doing anything else |
diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt index 2f625c108..d5a1c6317 100644 --- a/externals/nx_tzdb/CMakeLists.txt +++ b/externals/nx_tzdb/CMakeLists.txt | |||
| @@ -1,24 +1,60 @@ | |||
| 1 | # SPDX-FileCopyrightText: 2023 yuzu Emulator Project | 1 | # SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | set(NX_TZDB_VERSION "220816") | 4 | set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") |
| 5 | set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") | 5 | |
| 6 | add_library(nx_tzdb INTERFACE) | ||
| 7 | |||
| 8 | find_program(GIT git) | ||
| 9 | find_program(GNU_MAKE make) | ||
| 10 | find_program(GNU_DATE date) | ||
| 6 | 11 | ||
| 12 | set(CAN_BUILD_NX_TZDB true) | ||
| 13 | |||
| 14 | if (NOT GIT) | ||
| 15 | set(CAN_BUILD_NX_TZDB false) | ||
| 16 | endif() | ||
| 17 | if (NOT GNU_MAKE) | ||
| 18 | set(CAN_BUILD_NX_TZDB false) | ||
| 19 | endif() | ||
| 20 | if (NOT GNU_DATE) | ||
| 21 | set(CAN_BUILD_NX_TZDB false) | ||
| 22 | endif() | ||
| 23 | if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID) | ||
| 24 | # tzdb_to_nx currently requires a posix-compliant host | ||
| 25 | # MinGW and Android are handled here due to the executable format being different from the host system | ||
| 26 | # TODO (lat9nq): cross-compiling support | ||
| 27 | set(CAN_BUILD_NX_TZDB false) | ||
| 28 | endif() | ||
| 29 | |||
| 30 | set(NX_TZDB_VERSION "220816") | ||
| 7 | set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") | 31 | set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") |
| 8 | set(NX_TZDB_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") | ||
| 9 | 32 | ||
| 10 | set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") | 33 | set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") |
| 34 | |||
| 35 | if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE}) | ||
| 36 | set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") | ||
| 37 | |||
| 38 | message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...") | ||
| 39 | file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE} | ||
| 40 | STATUS NX_TZDB_DOWNLOAD_STATUS) | ||
| 41 | list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE) | ||
| 42 | if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0) | ||
| 43 | message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})") | ||
| 44 | endif() | ||
| 11 | 45 | ||
| 12 | if (NOT EXISTS ${NX_TZDB_ARCHIVE}) | ||
| 13 | file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE}) | ||
| 14 | file(ARCHIVE_EXTRACT | 46 | file(ARCHIVE_EXTRACT |
| 15 | INPUT | 47 | INPUT |
| 16 | ${NX_TZDB_ARCHIVE} | 48 | ${NX_TZDB_ARCHIVE} |
| 17 | DESTINATION | 49 | DESTINATION |
| 18 | ${NX_TZDB_DIR}) | 50 | ${NX_TZDB_ROMFS_DIR}) |
| 51 | elseif (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA) | ||
| 52 | add_subdirectory(tzdb_to_nx) | ||
| 53 | add_dependencies(nx_tzdb x80e) | ||
| 54 | |||
| 55 | set(NX_TZDB_ROMFS_DIR "${NX_TZDB_DIR}") | ||
| 19 | endif() | 56 | endif() |
| 20 | 57 | ||
| 21 | add_library(nx_tzdb INTERFACE) | ||
| 22 | target_include_directories(nx_tzdb | 58 | target_include_directories(nx_tzdb |
| 23 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include | 59 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include |
| 24 | INTERFACE ${NX_TZDB_INCLUDE_DIR}) | 60 | INTERFACE ${NX_TZDB_INCLUDE_DIR}) |
| @@ -41,25 +77,25 @@ function(CreateHeader ZONE_PATH HEADER_NAME) | |||
| 41 | target_sources(nx_tzdb PRIVATE ${HEADER_PATH}) | 77 | target_sources(nx_tzdb PRIVATE ${HEADER_PATH}) |
| 42 | endfunction() | 78 | endfunction() |
| 43 | 79 | ||
| 44 | CreateHeader(${NX_TZDB_DIR} base) | 80 | CreateHeader(${NX_TZDB_ROMFS_DIR} base) |
| 45 | CreateHeader(${NX_TZDB_DIR}/zoneinfo zoneinfo) | 81 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo) |
| 46 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Africa africa) | 82 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa) |
| 47 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/America america) | 83 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america) |
| 48 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Argentina america_argentina) | 84 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina) |
| 49 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Indiana america_indiana) | 85 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana) |
| 50 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Kentucky america_kentucky) | 86 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky) |
| 51 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/North_Dakota america_north_dakota) | 87 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota) |
| 52 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Antartica antartica) | 88 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica) |
| 53 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Arctic arctic) | 89 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic) |
| 54 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Asia asia) | 90 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia) |
| 55 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Atlantic atlantic) | 91 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic) |
| 56 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Australia australia) | 92 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia) |
| 57 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Brazil brazil) | 93 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil) |
| 58 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Canada canada) | 94 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada) |
| 59 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Chile chile) | 95 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile) |
| 60 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Etc etc) | 96 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc) |
| 61 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Europe europe) | 97 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe) |
| 62 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Indian indian) | 98 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian) |
| 63 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Mexico mexico) | 99 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico) |
| 64 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/Pacific pacific) | 100 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific) |
| 65 | CreateHeader(${NX_TZDB_DIR}/zoneinfo/US us) | 101 | CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/US us) |
diff --git a/externals/nx_tzdb/NxTzdbCreateHeader.cmake b/externals/nx_tzdb/NxTzdbCreateHeader.cmake index 69166aa5b..8c29e1167 100644 --- a/externals/nx_tzdb/NxTzdbCreateHeader.cmake +++ b/externals/nx_tzdb/NxTzdbCreateHeader.cmake | |||
| @@ -15,7 +15,7 @@ set(DIRECTORY_NAME ${HEADER_NAME}) | |||
| 15 | 15 | ||
| 16 | set(FILE_DATA "") | 16 | set(FILE_DATA "") |
| 17 | foreach(ZONE_FILE ${FILE_LIST}) | 17 | foreach(ZONE_FILE ${FILE_LIST}) |
| 18 | if ("${ZONE_FILE}" STREQUAL "\n") | 18 | if (ZONE_FILE STREQUAL "\n") |
| 19 | continue() | 19 | continue() |
| 20 | endif() | 20 | endif() |
| 21 | 21 | ||
| @@ -26,13 +26,13 @@ foreach(ZONE_FILE ${FILE_LIST}) | |||
| 26 | foreach(I RANGE 0 ${ZONE_DATA_LEN} 2) | 26 | foreach(I RANGE 0 ${ZONE_DATA_LEN} 2) |
| 27 | math(EXPR BREAK_LINE "(${I} + 2) % 38") | 27 | math(EXPR BREAK_LINE "(${I} + 2) % 38") |
| 28 | 28 | ||
| 29 | string(SUBSTRING "${ZONE_DATA}" "${I}" "2" HEX_DATA) | 29 | string(SUBSTRING "${ZONE_DATA}" "${I}" 2 HEX_DATA) |
| 30 | if ("${HEX_DATA}" STREQUAL "") | 30 | if (NOT HEX_DATA) |
| 31 | break() | 31 | break() |
| 32 | endif() | 32 | endif() |
| 33 | 33 | ||
| 34 | string(APPEND FILE_DATA "0x${HEX_DATA},") | 34 | string(APPEND FILE_DATA "0x${HEX_DATA},") |
| 35 | if ("${BREAK_LINE}" STREQUAL "0") | 35 | if (BREAK_LINE EQUAL 0) |
| 36 | string(APPEND FILE_DATA "\n") | 36 | string(APPEND FILE_DATA "\n") |
| 37 | else() | 37 | else() |
| 38 | string(APPEND FILE_DATA " ") | 38 | string(APPEND FILE_DATA " ") |
diff --git a/externals/nx_tzdb/include/nx_tzdb.h b/externals/nx_tzdb/include/nx_tzdb.h index d7b1e4304..1f7c6069a 100644 --- a/externals/nx_tzdb/include/nx_tzdb.h +++ b/externals/nx_tzdb/include/nx_tzdb.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "nx_tzdb/america_indiana.h" | 9 | #include "nx_tzdb/america_indiana.h" |
| 10 | #include "nx_tzdb/america_kentucky.h" | 10 | #include "nx_tzdb/america_kentucky.h" |
| 11 | #include "nx_tzdb/america_north_dakota.h" | 11 | #include "nx_tzdb/america_north_dakota.h" |
| 12 | #include "nx_tzdb/antartica.h" | 12 | #include "nx_tzdb/antarctica.h" |
| 13 | #include "nx_tzdb/arctic.h" | 13 | #include "nx_tzdb/arctic.h" |
| 14 | #include "nx_tzdb/asia.h" | 14 | #include "nx_tzdb/asia.h" |
| 15 | #include "nx_tzdb/atlantic.h" | 15 | #include "nx_tzdb/atlantic.h" |
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx new file mode 160000 | |||
| Subproject 34df65eff295c2bd9ee9e6a077d662486d5cabb | |||
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 7ae538cf9..bab4f4d0f 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -163,13 +163,14 @@ android { | |||
| 163 | tasks.getByPath("preBuild").dependsOn("ktlintCheck") | 163 | tasks.getByPath("preBuild").dependsOn("ktlintCheck") |
| 164 | 164 | ||
| 165 | ktlint { | 165 | ktlint { |
| 166 | version.set("0.47.0") | 166 | version.set("0.47.1") |
| 167 | android.set(true) | 167 | android.set(true) |
| 168 | ignoreFailures.set(false) | 168 | ignoreFailures.set(false) |
| 169 | disabledRules.set( | 169 | disabledRules.set( |
| 170 | setOf( | 170 | setOf( |
| 171 | "no-wildcard-imports", | 171 | "no-wildcard-imports", |
| 172 | "package-name" | 172 | "package-name", |
| 173 | "import-ordering" | ||
| 173 | ) | 174 | ) |
| 174 | ) | 175 | ) |
| 175 | reporters { | 176 | reporters { |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index a6f87fc2e..e31ad69e2 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -53,6 +53,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 53 | <activity | 53 | <activity |
| 54 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" | 54 | android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" |
| 55 | android:theme="@style/Theme.Yuzu.Main" | 55 | android:theme="@style/Theme.Yuzu.Main" |
| 56 | android:screenOrientation="userLandscape" | ||
| 56 | android:supportsPictureInPicture="true" | 57 | android:supportsPictureInPicture="true" |
| 57 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" | 58 | android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" |
| 58 | android:exported="true"> | 59 | android:exported="true"> |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 63b4df273..d41933766 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt | |||
| @@ -8,6 +8,9 @@ enum class BooleanSetting( | |||
| 8 | override val section: String, | 8 | override val section: String, |
| 9 | override val defaultValue: Boolean | 9 | override val defaultValue: Boolean |
| 10 | ) : AbstractBooleanSetting { | 10 | ) : AbstractBooleanSetting { |
| 11 | CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), | ||
| 12 | FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), | ||
| 13 | FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), | ||
| 11 | PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), | 14 | PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), |
| 12 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); | 15 | USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); |
| 13 | 16 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt index 63f95690c..6621289fd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt | |||
| @@ -8,6 +8,7 @@ enum class StringSetting( | |||
| 8 | override val section: String, | 8 | override val section: String, |
| 9 | override val defaultValue: String | 9 | override val defaultValue: String |
| 10 | ) : AbstractStringSetting { | 10 | ) : AbstractStringSetting { |
| 11 | AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), | ||
| 11 | CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); | 12 | CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); |
| 12 | 13 | ||
| 13 | override var string: String = defaultValue | 14 | override var string: String = defaultValue |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt index 0f8edbfb0..a67001311 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt | |||
| @@ -3,12 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.features.settings.model.view | 4 | package org.yuzu.yuzu_emu.features.settings.model.view |
| 5 | 5 | ||
| 6 | import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | ||
| 7 | |||
| 8 | class HeaderSetting( | 6 | class HeaderSetting( |
| 9 | setting: AbstractSetting?, | 7 | titleId: Int |
| 10 | titleId: Int, | 8 | ) : SettingsItem(null, titleId, 0) { |
| 11 | descriptionId: Int | ||
| 12 | ) : SettingsItem(setting, titleId, descriptionId) { | ||
| 13 | override val type = TYPE_HEADER | 9 | override val type = TYPE_HEADER |
| 14 | } | 10 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt index bad34fd88..3b6731dcd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt | |||
| @@ -7,20 +7,20 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting | |||
| 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting | 7 | import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting |
| 8 | 8 | ||
| 9 | class StringSingleChoiceSetting( | 9 | class StringSingleChoiceSetting( |
| 10 | val key: String? = null, | ||
| 11 | setting: AbstractSetting?, | 10 | setting: AbstractSetting?, |
| 12 | titleId: Int, | 11 | titleId: Int, |
| 13 | descriptionId: Int, | 12 | descriptionId: Int, |
| 14 | val choicesId: Array<String>, | 13 | val choices: Array<String>, |
| 15 | private val valuesId: Array<String>?, | 14 | val values: Array<String>?, |
| 15 | val key: String? = null, | ||
| 16 | private val defaultValue: String? = null | 16 | private val defaultValue: String? = null |
| 17 | ) : SettingsItem(setting, titleId, descriptionId) { | 17 | ) : SettingsItem(setting, titleId, descriptionId) { |
| 18 | override val type = TYPE_STRING_SINGLE_CHOICE | 18 | override val type = TYPE_STRING_SINGLE_CHOICE |
| 19 | 19 | ||
| 20 | fun getValueAt(index: Int): String? { | 20 | fun getValueAt(index: Int): String? { |
| 21 | if (valuesId == null) return null | 21 | if (values == null) return null |
| 22 | return if (index >= 0 && index < valuesId.size) { | 22 | return if (index >= 0 && index < values.size) { |
| 23 | valuesId[index] | 23 | values[index] |
| 24 | } else { | 24 | } else { |
| 25 | "" | 25 | "" |
| 26 | } | 26 | } |
| @@ -36,8 +36,8 @@ class StringSingleChoiceSetting( | |||
| 36 | val selectValueIndex: Int | 36 | val selectValueIndex: Int |
| 37 | get() { | 37 | get() { |
| 38 | val selectedValue = selectedValue | 38 | val selectedValue = selectedValue |
| 39 | for (i in valuesId!!.indices) { | 39 | for (i in values!!.indices) { |
| 40 | if (valuesId[i] == selectedValue) { | 40 | if (values[i] == selectedValue) { |
| 41 | return i | 41 | return i |
| 42 | } | 42 | } |
| 43 | } | 43 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt index eac6a134b..ce0b92c90 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt | |||
| @@ -138,7 +138,7 @@ class SettingsAdapter( | |||
| 138 | clickedItem = item | 138 | clickedItem = item |
| 139 | dialog = MaterialAlertDialogBuilder(context) | 139 | dialog = MaterialAlertDialogBuilder(context) |
| 140 | .setTitle(item.nameId) | 140 | .setTitle(item.nameId) |
| 141 | .setSingleChoiceItems(item.choicesId, item.selectValueIndex, this) | 141 | .setSingleChoiceItems(item.choices, item.selectValueIndex, this) |
| 142 | .show() | 142 | .show() |
| 143 | } | 143 | } |
| 144 | 144 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index c8c85dd7a..59c1d9d54 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt | |||
| @@ -42,7 +42,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | fun putSetting(setting: AbstractSetting) { | 44 | fun putSetting(setting: AbstractSetting) { |
| 45 | if (setting.section == null) { | 45 | if (setting.section == null || setting.key == null) { |
| 46 | return | 46 | return |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -353,18 +353,31 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 353 | 353 | ||
| 354 | private fun addAudioSettings(sl: ArrayList<SettingsItem>) { | 354 | private fun addAudioSettings(sl: ArrayList<SettingsItem>) { |
| 355 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) | 355 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) |
| 356 | sl.add( | 356 | sl.apply { |
| 357 | SliderSetting( | 357 | add( |
| 358 | IntSetting.AUDIO_VOLUME, | 358 | StringSingleChoiceSetting( |
| 359 | R.string.audio_volume, | 359 | StringSetting.AUDIO_OUTPUT_ENGINE, |
| 360 | R.string.audio_volume_description, | 360 | R.string.audio_output_engine, |
| 361 | 0, | 361 | 0, |
| 362 | 100, | 362 | settingsActivity.resources.getStringArray(R.array.outputEngineEntries), |
| 363 | "%", | 363 | settingsActivity.resources.getStringArray(R.array.outputEngineValues), |
| 364 | IntSetting.AUDIO_VOLUME.key, | 364 | StringSetting.AUDIO_OUTPUT_ENGINE.key, |
| 365 | IntSetting.AUDIO_VOLUME.defaultValue | 365 | StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue |
| 366 | ) | 366 | ) |
| 367 | ) | 367 | ) |
| 368 | add( | ||
| 369 | SliderSetting( | ||
| 370 | IntSetting.AUDIO_VOLUME, | ||
| 371 | R.string.audio_volume, | ||
| 372 | R.string.audio_volume_description, | ||
| 373 | 0, | ||
| 374 | 100, | ||
| 375 | "%", | ||
| 376 | IntSetting.AUDIO_VOLUME.key, | ||
| 377 | IntSetting.AUDIO_VOLUME.defaultValue | ||
| 378 | ) | ||
| 379 | ) | ||
| 380 | } | ||
| 368 | } | 381 | } |
| 369 | 382 | ||
| 370 | private fun addThemeSettings(sl: ArrayList<SettingsItem>) { | 383 | private fun addThemeSettings(sl: ArrayList<SettingsItem>) { |
| @@ -467,6 +480,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 467 | private fun addDebugSettings(sl: ArrayList<SettingsItem>) { | 480 | private fun addDebugSettings(sl: ArrayList<SettingsItem>) { |
| 468 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) | 481 | settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) |
| 469 | sl.apply { | 482 | sl.apply { |
| 483 | add(HeaderSetting(R.string.gpu)) | ||
| 470 | add( | 484 | add( |
| 471 | SingleChoiceSetting( | 485 | SingleChoiceSetting( |
| 472 | IntSetting.RENDERER_BACKEND, | 486 | IntSetting.RENDERER_BACKEND, |
| @@ -487,6 +501,39 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) | |||
| 487 | IntSetting.RENDERER_DEBUG.defaultValue | 501 | IntSetting.RENDERER_DEBUG.defaultValue |
| 488 | ) | 502 | ) |
| 489 | ) | 503 | ) |
| 504 | |||
| 505 | add(HeaderSetting(R.string.cpu)) | ||
| 506 | add( | ||
| 507 | SwitchSetting( | ||
| 508 | BooleanSetting.CPU_DEBUG_MODE, | ||
| 509 | R.string.cpu_debug_mode, | ||
| 510 | R.string.cpu_debug_mode_description, | ||
| 511 | BooleanSetting.CPU_DEBUG_MODE.key, | ||
| 512 | BooleanSetting.CPU_DEBUG_MODE.defaultValue | ||
| 513 | ) | ||
| 514 | ) | ||
| 515 | |||
| 516 | val fastmem = object : AbstractBooleanSetting { | ||
| 517 | override var boolean: Boolean | ||
| 518 | get() = | ||
| 519 | BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean | ||
| 520 | set(value) { | ||
| 521 | BooleanSetting.FASTMEM.boolean = value | ||
| 522 | BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value | ||
| 523 | } | ||
| 524 | override val key: String? = null | ||
| 525 | override val section: String = Settings.SECTION_CPU | ||
| 526 | override val isRuntimeEditable: Boolean = false | ||
| 527 | override val valueAsString: String = "" | ||
| 528 | override val defaultValue: Any = true | ||
| 529 | } | ||
| 530 | add( | ||
| 531 | SwitchSetting( | ||
| 532 | fastmem, | ||
| 533 | R.string.fastmem, | ||
| 534 | 0 | ||
| 535 | ) | ||
| 536 | ) | ||
| 490 | } | 537 | } |
| 491 | } | 538 | } |
| 492 | } | 539 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt index de764a27f..e4e321bd3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt | |||
| @@ -26,6 +26,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti | |||
| 26 | for (i in values.indices) { | 26 | for (i in values.indices) { |
| 27 | if (values[i] == item.selectedValue) { | 27 | if (values[i] == item.selectedValue) { |
| 28 | binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] | 28 | binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i] |
| 29 | return | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } else if (item is StringSingleChoiceSetting) { | ||
| 33 | for (i in item.values!!.indices) { | ||
| 34 | if (item.values[i] == item.selectedValue) { | ||
| 35 | binding.textSettingDescription.text = item.choices[i] | ||
| 36 | return | ||
| 29 | } | 37 | } |
| 30 | } | 38 | } |
| 31 | } else { | 39 | } else { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt index 20a0636df..70a52df5d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt | |||
| @@ -244,5 +244,21 @@ object SettingsFile { | |||
| 244 | val setting = settings[key] | 244 | val setting = settings[key] |
| 245 | parser.put(header, setting!!.key, setting.valueAsString) | 245 | parser.put(header, setting!!.key, setting.valueAsString) |
| 246 | } | 246 | } |
| 247 | |||
| 248 | BooleanSetting.values().forEach { | ||
| 249 | if (!keySet.contains(it.key)) { | ||
| 250 | parser.put(header, it.key, it.valueAsString) | ||
| 251 | } | ||
| 252 | } | ||
| 253 | IntSetting.values().forEach { | ||
| 254 | if (!keySet.contains(it.key)) { | ||
| 255 | parser.put(header, it.key, it.valueAsString) | ||
| 256 | } | ||
| 257 | } | ||
| 258 | StringSetting.values().forEach { | ||
| 259 | if (!keySet.contains(it.key)) { | ||
| 260 | parser.put(header, it.key, it.valueAsString) | ||
| 261 | } | ||
| 262 | } | ||
| 247 | } | 263 | } |
| 248 | } | 264 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 42e2e5b75..4643418c1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -85,20 +85,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 85 | onReturnFromSettings = context.activityResultRegistry.register( | 85 | onReturnFromSettings = context.activityResultRegistry.register( |
| 86 | "SettingsResult", | 86 | "SettingsResult", |
| 87 | ActivityResultContracts.StartActivityForResult() | 87 | ActivityResultContracts.StartActivityForResult() |
| 88 | ) { | 88 | ) { updateScreenLayout() } |
| 89 | binding.surfaceEmulation.setAspectRatio( | ||
| 90 | when (IntSetting.RENDERER_ASPECT_RATIO.int) { | ||
| 91 | 0 -> Rational(16, 9) | ||
| 92 | 1 -> Rational(4, 3) | ||
| 93 | 2 -> Rational(21, 9) | ||
| 94 | 3 -> Rational(16, 10) | ||
| 95 | 4 -> null // Stretch | ||
| 96 | else -> Rational(16, 9) | ||
| 97 | } | ||
| 98 | ) | ||
| 99 | emulationActivity?.buildPictureInPictureParams() | ||
| 100 | updateScreenLayout() | ||
| 101 | } | ||
| 102 | } else { | 89 | } else { |
| 103 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") | 90 | throw IllegalStateException("EmulationFragment must have EmulationActivity parent") |
| 104 | } | 91 | } |
| @@ -242,17 +229,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 242 | DirectoryInitialization.start(requireContext()) | 229 | DirectoryInitialization.start(requireContext()) |
| 243 | } | 230 | } |
| 244 | 231 | ||
| 245 | binding.surfaceEmulation.setAspectRatio( | ||
| 246 | when (IntSetting.RENDERER_ASPECT_RATIO.int) { | ||
| 247 | 0 -> Rational(16, 9) | ||
| 248 | 1 -> Rational(4, 3) | ||
| 249 | 2 -> Rational(21, 9) | ||
| 250 | 3 -> Rational(16, 10) | ||
| 251 | 4 -> null // Stretch | ||
| 252 | else -> Rational(16, 9) | ||
| 253 | } | ||
| 254 | ) | ||
| 255 | |||
| 256 | updateScreenLayout() | 232 | updateScreenLayout() |
| 257 | 233 | ||
| 258 | emulationState.run(emulationActivity!!.isActivityRecreated) | 234 | emulationState.run(emulationActivity!!.isActivityRecreated) |
| @@ -315,7 +291,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 315 | } | 291 | } |
| 316 | 292 | ||
| 317 | @SuppressLint("SourceLockedOrientationActivity") | 293 | @SuppressLint("SourceLockedOrientationActivity") |
| 318 | private fun updateScreenLayout() { | 294 | private fun updateOrientation() { |
| 319 | emulationActivity?.let { | 295 | emulationActivity?.let { |
| 320 | it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) { | 296 | it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) { |
| 321 | Settings.LayoutOption_MobileLandscape -> | 297 | Settings.LayoutOption_MobileLandscape -> |
| @@ -326,7 +302,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 326 | else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE | 302 | else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE |
| 327 | } | 303 | } |
| 328 | } | 304 | } |
| 329 | onConfigurationChanged(resources.configuration) | 305 | } |
| 306 | |||
| 307 | private fun updateScreenLayout() { | ||
| 308 | binding.surfaceEmulation.setAspectRatio( | ||
| 309 | when (IntSetting.RENDERER_ASPECT_RATIO.int) { | ||
| 310 | 0 -> Rational(16, 9) | ||
| 311 | 1 -> Rational(4, 3) | ||
| 312 | 2 -> Rational(21, 9) | ||
| 313 | 3 -> Rational(16, 10) | ||
| 314 | 4 -> null // Stretch | ||
| 315 | else -> Rational(16, 9) | ||
| 316 | } | ||
| 317 | ) | ||
| 318 | emulationActivity?.buildPictureInPictureParams() | ||
| 319 | updateOrientation() | ||
| 330 | } | 320 | } |
| 331 | 321 | ||
| 332 | private fun updateFoldableLayout( | 322 | private fun updateFoldableLayout( |
| @@ -359,7 +349,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 359 | binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 349 | binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 360 | binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT | 350 | binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT |
| 361 | isInFoldableLayout = false | 351 | isInFoldableLayout = false |
| 362 | updateScreenLayout() | 352 | updateOrientation() |
| 353 | onConfigurationChanged(resources.configuration) | ||
| 363 | } | 354 | } |
| 364 | binding.emulationContainer.requestLayout() | 355 | binding.emulationContainer.requestLayout() |
| 365 | binding.inputContainer.requestLayout() | 356 | binding.inputContainer.requestLayout() |
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml index 599d845ad..a5767adee 100644 --- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml +++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | 2 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 3 | xmlns:tools="http://schemas.android.com/tools" | 4 | xmlns:tools="http://schemas.android.com/tools" |
| 4 | android:layout_width="match_parent" | 5 | android:layout_width="match_parent" |
| 5 | android:layout_height="wrap_content" | 6 | android:layout_height="wrap_content" |
| 6 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 7 | android:background="?android:attr/selectableItemBackground" | 7 | android:background="?android:attr/selectableItemBackground" |
| 8 | android:clickable="true" | 8 | android:clickable="true" |
| 9 | android:focusable="true" | 9 | android:focusable="true" |
| 10 | android:minHeight="72dp" | 10 | android:minHeight="72dp" |
| 11 | android:paddingVertical="@dimen/spacing_large" | ||
| 11 | android:paddingStart="@dimen/spacing_large" | 12 | android:paddingStart="@dimen/spacing_large" |
| 12 | android:paddingEnd="24dp" | 13 | android:paddingEnd="24dp"> |
| 13 | android:paddingVertical="@dimen/spacing_large"> | ||
| 14 | 14 | ||
| 15 | <com.google.android.material.materialswitch.MaterialSwitch | 15 | <com.google.android.material.materialswitch.MaterialSwitch |
| 16 | android:id="@+id/switch_widget" | 16 | android:id="@+id/switch_widget" |
| @@ -19,32 +19,35 @@ | |||
| 19 | android:layout_alignParentEnd="true" | 19 | android:layout_alignParentEnd="true" |
| 20 | android:layout_centerVertical="true" /> | 20 | android:layout_centerVertical="true" /> |
| 21 | 21 | ||
| 22 | <com.google.android.material.textview.MaterialTextView | 22 | <LinearLayout |
| 23 | style="@style/TextAppearance.Material3.BodySmall" | 23 | android:layout_width="match_parent" |
| 24 | android:id="@+id/text_setting_description" | ||
| 25 | android:layout_width="wrap_content" | ||
| 26 | android:layout_height="wrap_content" | ||
| 27 | android:layout_alignParentStart="true" | ||
| 28 | android:layout_alignStart="@+id/text_setting_name" | ||
| 29 | android:layout_below="@+id/text_setting_name" | ||
| 30 | android:layout_marginEnd="@dimen/spacing_large" | ||
| 31 | android:layout_marginTop="@dimen/spacing_small" | ||
| 32 | android:layout_toStartOf="@+id/switch_widget" | ||
| 33 | android:textAlignment="viewStart" | ||
| 34 | tools:text="@string/frame_limit_enable_description" /> | ||
| 35 | |||
| 36 | <com.google.android.material.textview.MaterialTextView | ||
| 37 | style="@style/TextAppearance.Material3.HeadlineMedium" | ||
| 38 | android:id="@+id/text_setting_name" | ||
| 39 | android:layout_width="0dp" | ||
| 40 | android:layout_height="wrap_content" | 24 | android:layout_height="wrap_content" |
| 41 | android:layout_alignParentStart="true" | ||
| 42 | android:layout_alignParentTop="true" | 25 | android:layout_alignParentTop="true" |
| 26 | android:layout_centerVertical="true" | ||
| 43 | android:layout_marginEnd="@dimen/spacing_large" | 27 | android:layout_marginEnd="@dimen/spacing_large" |
| 44 | android:layout_toStartOf="@+id/switch_widget" | 28 | android:layout_toStartOf="@+id/switch_widget" |
| 45 | android:textSize="16sp" | 29 | android:gravity="center_vertical" |
| 46 | android:textAlignment="viewStart" | 30 | android:orientation="vertical"> |
| 47 | app:lineHeight="28dp" | 31 | |
| 48 | tools:text="@string/frame_limit_enable" /> | 32 | <com.google.android.material.textview.MaterialTextView |
| 33 | android:id="@+id/text_setting_name" | ||
| 34 | style="@style/TextAppearance.Material3.HeadlineMedium" | ||
| 35 | android:layout_width="wrap_content" | ||
| 36 | android:layout_height="wrap_content" | ||
| 37 | android:textAlignment="viewStart" | ||
| 38 | android:textSize="16sp" | ||
| 39 | app:lineHeight="28dp" | ||
| 40 | tools:text="@string/frame_limit_enable" /> | ||
| 41 | |||
| 42 | <com.google.android.material.textview.MaterialTextView | ||
| 43 | android:id="@+id/text_setting_description" | ||
| 44 | style="@style/TextAppearance.Material3.BodySmall" | ||
| 45 | android:layout_width="wrap_content" | ||
| 46 | android:layout_height="wrap_content" | ||
| 47 | android:layout_marginTop="@dimen/spacing_small" | ||
| 48 | android:textAlignment="viewStart" | ||
| 49 | tools:text="@string/frame_limit_enable_description" /> | ||
| 50 | |||
| 51 | </LinearLayout> | ||
| 49 | 52 | ||
| 50 | </RelativeLayout> | 53 | </RelativeLayout> |
diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml index abd24df6f..cf85bc0da 100644 --- a/src/android/app/src/main/res/layout/list_item_settings_header.xml +++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml | |||
| @@ -1,20 +1,14 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | 2 | <com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:tools="http://schemas.android.com/tools" | 3 | xmlns:tools="http://schemas.android.com/tools" |
| 4 | android:id="@+id/text_header_name" | ||
| 5 | style="@style/TextAppearance.Material3.TitleSmall" | ||
| 4 | android:layout_width="match_parent" | 6 | android:layout_width="match_parent" |
| 5 | android:layout_height="48dp" | 7 | android:layout_height="wrap_content" |
| 6 | android:paddingVertical="4dp" | 8 | android:layout_gravity="start|center_vertical" |
| 7 | android:paddingHorizontal="@dimen/spacing_large"> | 9 | android:paddingHorizontal="@dimen/spacing_large" |
| 8 | 10 | android:paddingVertical="16dp" | |
| 9 | <com.google.android.material.textview.MaterialTextView | 11 | android:textAlignment="viewStart" |
| 10 | style="@style/TextAppearance.Material3.TitleSmall" | 12 | android:textColor="?attr/colorPrimary" |
| 11 | android:id="@+id/text_header_name" | 13 | android:textStyle="bold" |
| 12 | android:layout_width="match_parent" | 14 | tools:text="CPU Settings" /> |
| 13 | android:layout_height="wrap_content" | ||
| 14 | android:layout_gravity="start|center_vertical" | ||
| 15 | android:textColor="?attr/colorPrimary" | ||
| 16 | android:textAlignment="viewStart" | ||
| 17 | android:textStyle="bold" | ||
| 18 | tools:text="CPU Settings" /> | ||
| 19 | |||
| 20 | </FrameLayout> | ||
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 7f7b1938c..6d092f7a9 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml | |||
| @@ -236,4 +236,15 @@ | |||
| 236 | <item>2</item> | 236 | <item>2</item> |
| 237 | </integer-array> | 237 | </integer-array> |
| 238 | 238 | ||
| 239 | <string-array name="outputEngineEntries"> | ||
| 240 | <item>@string/auto</item> | ||
| 241 | <item>@string/cubeb</item> | ||
| 242 | <item>@string/string_null</item> | ||
| 243 | </string-array> | ||
| 244 | <string-array name="outputEngineValues"> | ||
| 245 | <item>auto</item> | ||
| 246 | <item>cubeb</item> | ||
| 247 | <item>null</item> | ||
| 248 | </string-array> | ||
| 249 | |||
| 239 | </resources> | 250 | </resources> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2f2059d42..cc1d8c39d 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -158,7 +158,6 @@ | |||
| 158 | <string name="set_custom_rtc">Set custom RTC</string> | 158 | <string name="set_custom_rtc">Set custom RTC</string> |
| 159 | 159 | ||
| 160 | <!-- Graphics settings strings --> | 160 | <!-- Graphics settings strings --> |
| 161 | <string name="renderer_api">API</string> | ||
| 162 | <string name="renderer_accuracy">Accuracy level</string> | 161 | <string name="renderer_accuracy">Accuracy level</string> |
| 163 | <string name="renderer_resolution">Resolution (Handheld/Docked)</string> | 162 | <string name="renderer_resolution">Resolution (Handheld/Docked)</string> |
| 164 | <string name="renderer_vsync">VSync mode</string> | 163 | <string name="renderer_vsync">VSync mode</string> |
| @@ -172,12 +171,21 @@ | |||
| 172 | <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string> | 171 | <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string> |
| 173 | <string name="renderer_reactive_flushing">Use reactive flushing</string> | 172 | <string name="renderer_reactive_flushing">Use reactive flushing</string> |
| 174 | <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> | 173 | <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> |
| 175 | <string name="renderer_debug">Graphics debugging</string> | ||
| 176 | <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string> | ||
| 177 | <string name="use_disk_shader_cache">Disk shader cache</string> | 174 | <string name="use_disk_shader_cache">Disk shader cache</string> |
| 178 | <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> | 175 | <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> |
| 179 | 176 | ||
| 177 | <!-- Debug settings strings --> | ||
| 178 | <string name="cpu">CPU</string> | ||
| 179 | <string name="cpu_debug_mode">CPU Debugging</string> | ||
| 180 | <string name="cpu_debug_mode_description">Puts the CPU in a slow debugging mode.</string> | ||
| 181 | <string name="gpu">GPU</string> | ||
| 182 | <string name="renderer_api">API</string> | ||
| 183 | <string name="renderer_debug">Graphics debugging</string> | ||
| 184 | <string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string> | ||
| 185 | <string name="fastmem">Fastmem</string> | ||
| 186 | |||
| 180 | <!-- Audio settings strings --> | 187 | <!-- Audio settings strings --> |
| 188 | <string name="audio_output_engine">Output engine</string> | ||
| 181 | <string name="audio_volume">Volume</string> | 189 | <string name="audio_volume">Volume</string> |
| 182 | <string name="audio_volume_description">Specifies the volume of audio output.</string> | 190 | <string name="audio_volume_description">Specifies the volume of audio output.</string> |
| 183 | 191 | ||
| @@ -196,6 +204,7 @@ | |||
| 196 | <string name="learn_more">Learn more</string> | 204 | <string name="learn_more">Learn more</string> |
| 197 | <string name="auto">Auto</string> | 205 | <string name="auto">Auto</string> |
| 198 | <string name="submit">Submit</string> | 206 | <string name="submit">Submit</string> |
| 207 | <string name="string_null">Null</string> | ||
| 199 | 208 | ||
| 200 | <!-- GPU driver installation --> | 209 | <!-- GPU driver installation --> |
| 201 | <string name="select_gpu_driver">Select GPU driver</string> | 210 | <string name="select_gpu_driver">Select GPU driver</string> |
| @@ -366,6 +375,9 @@ | |||
| 366 | <string name="theme_mode_light">Light</string> | 375 | <string name="theme_mode_light">Light</string> |
| 367 | <string name="theme_mode_dark">Dark</string> | 376 | <string name="theme_mode_dark">Dark</string> |
| 368 | 377 | ||
| 378 | <!-- Audio output engines --> | ||
| 379 | <string name="cubeb">cubeb</string> | ||
| 380 | |||
| 369 | <!-- Black backgrounds theme --> | 381 | <!-- Black backgrounds theme --> |
| 370 | <string name="use_black_backgrounds">Black backgrounds</string> | 382 | <string name="use_black_backgrounds">Black backgrounds</string> |
| 371 | <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> | 383 | <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> |
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index ceb0b41c6..7c17bbefa 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp | |||
| @@ -15,7 +15,7 @@ namespace FileSys::SystemArchive { | |||
| 15 | const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&> | 15 | const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&> |
| 16 | tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa}, | 16 | tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa}, |
| 17 | {"America", NxTzdb::america}, | 17 | {"America", NxTzdb::america}, |
| 18 | {"Antartica", NxTzdb::antartica}, | 18 | {"Antarctica", NxTzdb::antarctica}, |
| 19 | {"Arctic", NxTzdb::arctic}, | 19 | {"Arctic", NxTzdb::arctic}, |
| 20 | {"Asia", NxTzdb::asia}, | 20 | {"Asia", NxTzdb::asia}, |
| 21 | {"Atlantic", NxTzdb::atlantic}, | 21 | {"Atlantic", NxTzdb::atlantic}, |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 70480b725..908811e2c 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <condition_variable> | ||
| 8 | #include <mutex> | ||
| 7 | #include <optional> | 9 | #include <optional> |
| 8 | #include <vector> | 10 | #include <vector> |
| 9 | 11 | ||
| @@ -1313,7 +1315,8 @@ void KThread::RequestDummyThreadWait() { | |||
| 1313 | ASSERT(this->IsDummyThread()); | 1315 | ASSERT(this->IsDummyThread()); |
| 1314 | 1316 | ||
| 1315 | // We will block when the scheduler lock is released. | 1317 | // We will block when the scheduler lock is released. |
| 1316 | m_dummy_thread_runnable.store(false); | 1318 | std::scoped_lock lock{m_dummy_thread_mutex}; |
| 1319 | m_dummy_thread_runnable = false; | ||
| 1317 | } | 1320 | } |
| 1318 | 1321 | ||
| 1319 | void KThread::DummyThreadBeginWait() { | 1322 | void KThread::DummyThreadBeginWait() { |
| @@ -1323,7 +1326,8 @@ void KThread::DummyThreadBeginWait() { | |||
| 1323 | } | 1326 | } |
| 1324 | 1327 | ||
| 1325 | // Block until runnable is no longer false. | 1328 | // Block until runnable is no longer false. |
| 1326 | m_dummy_thread_runnable.wait(false); | 1329 | std::unique_lock lock{m_dummy_thread_mutex}; |
| 1330 | m_dummy_thread_cv.wait(lock, [this] { return m_dummy_thread_runnable; }); | ||
| 1327 | } | 1331 | } |
| 1328 | 1332 | ||
| 1329 | void KThread::DummyThreadEndWait() { | 1333 | void KThread::DummyThreadEndWait() { |
| @@ -1331,8 +1335,11 @@ void KThread::DummyThreadEndWait() { | |||
| 1331 | ASSERT(this->IsDummyThread()); | 1335 | ASSERT(this->IsDummyThread()); |
| 1332 | 1336 | ||
| 1333 | // Wake up the waiting thread. | 1337 | // Wake up the waiting thread. |
| 1334 | m_dummy_thread_runnable.store(true); | 1338 | { |
| 1335 | m_dummy_thread_runnable.notify_one(); | 1339 | std::scoped_lock lock{m_dummy_thread_mutex}; |
| 1340 | m_dummy_thread_runnable = true; | ||
| 1341 | } | ||
| 1342 | m_dummy_thread_cv.notify_one(); | ||
| 1336 | } | 1343 | } |
| 1337 | 1344 | ||
| 1338 | void KThread::BeginWait(KThreadQueue* queue) { | 1345 | void KThread::BeginWait(KThreadQueue* queue) { |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f9814ac8f..37fe5db77 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -892,7 +892,9 @@ private: | |||
| 892 | std::shared_ptr<Common::Fiber> m_host_context{}; | 892 | std::shared_ptr<Common::Fiber> m_host_context{}; |
| 893 | ThreadType m_thread_type{}; | 893 | ThreadType m_thread_type{}; |
| 894 | StepState m_step_state{}; | 894 | StepState m_step_state{}; |
| 895 | std::atomic<bool> m_dummy_thread_runnable{true}; | 895 | bool m_dummy_thread_runnable{true}; |
| 896 | std::mutex m_dummy_thread_mutex{}; | ||
| 897 | std::condition_variable m_dummy_thread_cv{}; | ||
| 896 | 898 | ||
| 897 | // For debugging | 899 | // For debugging |
| 898 | std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{}; | 900 | std::vector<KSynchronizationObject*> m_wait_objects_for_debugging{}; |
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp index b2bcb68c3..bc232c334 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp | |||
| @@ -36,12 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | |||
| 36 | 36 | ||
| 37 | // Validate UUID | 37 | // Validate UUID |
| 38 | constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` | 38 | constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` |
| 39 | if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != | 39 | if ((CT ^ ntag_file.uuid.part1[0] ^ ntag_file.uuid.part1[1] ^ ntag_file.uuid.part1[2]) != |
| 40 | ntag_file.uuid.uid[3]) { | 40 | ntag_file.uuid.crc_check1) { |
| 41 | return false; | 41 | return false; |
| 42 | } | 42 | } |
| 43 | if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ | 43 | if ((ntag_file.uuid.part2[0] ^ ntag_file.uuid.part2[1] ^ ntag_file.uuid.part2[2] ^ |
| 44 | ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { | 44 | ntag_file.uuid.nintendo_id) != ntag_file.uuid_crc_check2) { |
| 45 | return false; | 45 | return false; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| @@ -74,8 +74,9 @@ bool IsAmiiboValid(const NTAG215File& ntag_file) { | |||
| 74 | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | 74 | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { |
| 75 | NTAG215File encoded_data{}; | 75 | NTAG215File encoded_data{}; |
| 76 | 76 | ||
| 77 | encoded_data.uid = nfc_data.uuid.uid; | 77 | encoded_data.uid = nfc_data.uuid; |
| 78 | encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; | 78 | encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2; |
| 79 | encoded_data.internal_number = nfc_data.internal_number; | ||
| 79 | encoded_data.static_lock = nfc_data.static_lock; | 80 | encoded_data.static_lock = nfc_data.static_lock; |
| 80 | encoded_data.compability_container = nfc_data.compability_container; | 81 | encoded_data.compability_container = nfc_data.compability_container; |
| 81 | encoded_data.hmac_data = nfc_data.user_memory.hmac_data; | 82 | encoded_data.hmac_data = nfc_data.user_memory.hmac_data; |
| @@ -94,7 +95,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | |||
| 94 | encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; | 95 | encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc; |
| 95 | encoded_data.application_area = nfc_data.user_memory.application_area; | 96 | encoded_data.application_area = nfc_data.user_memory.application_area; |
| 96 | encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; | 97 | encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; |
| 97 | encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; | ||
| 98 | encoded_data.model_info = nfc_data.user_memory.model_info; | 98 | encoded_data.model_info = nfc_data.user_memory.model_info; |
| 99 | encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; | 99 | encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; |
| 100 | encoded_data.dynamic_lock = nfc_data.dynamic_lock; | 100 | encoded_data.dynamic_lock = nfc_data.dynamic_lock; |
| @@ -108,9 +108,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | |||
| 108 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | 108 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { |
| 109 | EncryptedNTAG215File nfc_data{}; | 109 | EncryptedNTAG215File nfc_data{}; |
| 110 | 110 | ||
| 111 | nfc_data.uuid.uid = encoded_data.uid; | 111 | nfc_data.uuid = encoded_data.uid; |
| 112 | nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; | 112 | nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2; |
| 113 | nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; | 113 | nfc_data.internal_number = encoded_data.internal_number; |
| 114 | nfc_data.static_lock = encoded_data.static_lock; | 114 | nfc_data.static_lock = encoded_data.static_lock; |
| 115 | nfc_data.compability_container = encoded_data.compability_container; | 115 | nfc_data.compability_container = encoded_data.compability_container; |
| 116 | nfc_data.user_memory.hmac_data = encoded_data.hmac_data; | 116 | nfc_data.user_memory.hmac_data = encoded_data.hmac_data; |
| @@ -139,23 +139,12 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | |||
| 139 | return nfc_data; | 139 | return nfc_data; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | u32 GetTagPassword(const TagUuid& uuid) { | ||
| 143 | // Verify that the generated password is correct | ||
| 144 | u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); | ||
| 145 | password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; | ||
| 146 | password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; | ||
| 147 | password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; | ||
| 148 | return password; | ||
| 149 | } | ||
| 150 | |||
| 151 | HashSeed GetSeed(const NTAG215File& data) { | 142 | HashSeed GetSeed(const NTAG215File& data) { |
| 152 | HashSeed seed{ | 143 | HashSeed seed{ |
| 153 | .magic = data.write_counter, | 144 | .magic = data.write_counter, |
| 154 | .padding = {}, | 145 | .padding = {}, |
| 155 | .uid_1 = data.uid, | 146 | .uid_1 = data.uid, |
| 156 | .nintendo_id_1 = data.nintendo_id, | ||
| 157 | .uid_2 = data.uid, | 147 | .uid_2 = data.uid, |
| 158 | .nintendo_id_2 = data.nintendo_id, | ||
| 159 | .keygen_salt = data.keygen_salt, | 148 | .keygen_salt = data.keygen_salt, |
| 160 | }; | 149 | }; |
| 161 | 150 | ||
| @@ -177,10 +166,11 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed | |||
| 177 | output.insert(output.end(), key.magic_bytes.begin(), | 166 | output.insert(output.end(), key.magic_bytes.begin(), |
| 178 | key.magic_bytes.begin() + key.magic_length); | 167 | key.magic_bytes.begin() + key.magic_length); |
| 179 | 168 | ||
| 180 | output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); | 169 | std::array<u8, sizeof(NFP::TagUuid)> seed_uuid{}; |
| 181 | output.emplace_back(seed.nintendo_id_1); | 170 | memcpy(seed_uuid.data(), &seed.uid_1, sizeof(NFP::TagUuid)); |
| 182 | output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); | 171 | output.insert(output.end(), seed_uuid.begin(), seed_uuid.end()); |
| 183 | output.emplace_back(seed.nintendo_id_2); | 172 | memcpy(seed_uuid.data(), &seed.uid_2, sizeof(NFP::TagUuid)); |
| 173 | output.insert(output.end(), seed_uuid.begin(), seed_uuid.end()); | ||
| 184 | 174 | ||
| 185 | for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { | 175 | for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { |
| 186 | output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); | 176 | output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); |
| @@ -264,8 +254,8 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou | |||
| 264 | 254 | ||
| 265 | // Copy the rest of the data directly | 255 | // Copy the rest of the data directly |
| 266 | out_data.uid = in_data.uid; | 256 | out_data.uid = in_data.uid; |
| 267 | out_data.nintendo_id = in_data.nintendo_id; | 257 | out_data.uid_crc_check2 = in_data.uid_crc_check2; |
| 268 | out_data.lock_bytes = in_data.lock_bytes; | 258 | out_data.internal_number = in_data.internal_number; |
| 269 | out_data.static_lock = in_data.static_lock; | 259 | out_data.static_lock = in_data.static_lock; |
| 270 | out_data.compability_container = in_data.compability_container; | 260 | out_data.compability_container = in_data.compability_container; |
| 271 | 261 | ||
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h index bf3044ed9..6a3e0841e 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.h +++ b/src/core/hle/service/nfc/common/amiibo_crypto.h | |||
| @@ -24,10 +24,8 @@ using DrgbOutput = std::array<u8, 0x20>; | |||
| 24 | struct HashSeed { | 24 | struct HashSeed { |
| 25 | u16_be magic; | 25 | u16_be magic; |
| 26 | std::array<u8, 0xE> padding; | 26 | std::array<u8, 0xE> padding; |
| 27 | NFC::UniqueSerialNumber uid_1; | 27 | TagUuid uid_1; |
| 28 | u8 nintendo_id_1; | 28 | TagUuid uid_2; |
| 29 | NFC::UniqueSerialNumber uid_2; | ||
| 30 | u8 nintendo_id_2; | ||
| 31 | std::array<u8, 0x20> keygen_salt; | 29 | std::array<u8, 0x20> keygen_salt; |
| 32 | }; | 30 | }; |
| 33 | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); | 31 | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); |
| @@ -69,9 +67,6 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data); | |||
| 69 | /// Converts from encoded file format to encrypted file format | 67 | /// Converts from encoded file format to encrypted file format |
| 70 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); | 68 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); |
| 71 | 69 | ||
| 72 | /// Returns password needed to allow write access to protected memory | ||
| 73 | u32 GetTagPassword(const TagUuid& uuid); | ||
| 74 | |||
| 75 | // Generates Seed needed for key derivation | 70 | // Generates Seed needed for key derivation |
| 76 | HashSeed GetSeed(const NTAG215File& data); | 71 | HashSeed GetSeed(const NTAG215File& data); |
| 77 | 72 | ||
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index b14f682b5..f4b180b06 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp | |||
| @@ -242,34 +242,39 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { | |||
| 242 | return ResultWrongDeviceState; | 242 | return ResultWrongDeviceState; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | UniqueSerialNumber uuid = encrypted_tag_data.uuid.uid; | 245 | UniqueSerialNumber uuid{}; |
| 246 | 246 | u8 uuid_length{}; | |
| 247 | // Generate random UUID to bypass amiibo load limits | 247 | NfcProtocol protocol{NfcProtocol::TypeA}; |
| 248 | if (Settings::values.random_amiibo_id) { | 248 | TagType tag_type{TagType::Type2}; |
| 249 | Common::TinyMT rng{}; | ||
| 250 | rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); | ||
| 251 | rng.GenerateRandomBytes(uuid.data(), sizeof(UniqueSerialNumber)); | ||
| 252 | uuid[3] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]; | ||
| 253 | } | ||
| 254 | 249 | ||
| 255 | if (is_mifare) { | 250 | if (is_mifare) { |
| 256 | tag_info = { | 251 | tag_type = TagType::Mifare; |
| 257 | .uuid = uuid, | 252 | uuid_length = sizeof(NFP::NtagTagUuid); |
| 258 | .uuid_extension = {}, | 253 | memcpy(uuid.data(), mifare_data.data(), uuid_length); |
| 259 | .uuid_length = static_cast<u8>(uuid.size()), | 254 | } else { |
| 260 | .protocol = NfcProtocol::TypeA, | 255 | tag_type = TagType::Type2; |
| 261 | .tag_type = TagType::Type4, | 256 | uuid_length = sizeof(NFP::NtagTagUuid); |
| 257 | NFP::NtagTagUuid nUuid{ | ||
| 258 | .part1 = encrypted_tag_data.uuid.part1, | ||
| 259 | .part2 = encrypted_tag_data.uuid.part2, | ||
| 260 | .nintendo_id = encrypted_tag_data.uuid.nintendo_id, | ||
| 262 | }; | 261 | }; |
| 263 | return ResultSuccess; | 262 | memcpy(uuid.data(), &nUuid, uuid_length); |
| 263 | |||
| 264 | // Generate random UUID to bypass amiibo load limits | ||
| 265 | if (Settings::values.random_amiibo_id) { | ||
| 266 | Common::TinyMT rng{}; | ||
| 267 | rng.Initialize(static_cast<u32>(GetCurrentPosixTime())); | ||
| 268 | rng.GenerateRandomBytes(uuid.data(), uuid_length); | ||
| 269 | } | ||
| 264 | } | 270 | } |
| 265 | 271 | ||
| 266 | // Protocol and tag type may change here | 272 | // Protocol and tag type may change here |
| 267 | tag_info = { | 273 | tag_info = { |
| 268 | .uuid = uuid, | 274 | .uuid = uuid, |
| 269 | .uuid_extension = {}, | 275 | .uuid_length = uuid_length, |
| 270 | .uuid_length = static_cast<u8>(uuid.size()), | 276 | .protocol = protocol, |
| 271 | .protocol = NfcProtocol::TypeA, | 277 | .tag_type = tag_type, |
| 272 | .tag_type = TagType::Type2, | ||
| 273 | }; | 278 | }; |
| 274 | 279 | ||
| 275 | return ResultSuccess; | 280 | return ResultSuccess; |
| @@ -277,8 +282,38 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { | |||
| 277 | 282 | ||
| 278 | Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters, | 283 | Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters, |
| 279 | std::span<MifareReadBlockData> read_block_data) const { | 284 | std::span<MifareReadBlockData> read_block_data) const { |
| 285 | if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { | ||
| 286 | LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); | ||
| 287 | if (device_state == DeviceState::TagRemoved) { | ||
| 288 | return ResultTagRemoved; | ||
| 289 | } | ||
| 290 | return ResultWrongDeviceState; | ||
| 291 | } | ||
| 292 | |||
| 280 | Result result = ResultSuccess; | 293 | Result result = ResultSuccess; |
| 281 | 294 | ||
| 295 | TagInfo tag_info{}; | ||
| 296 | result = GetTagInfo(tag_info, true); | ||
| 297 | |||
| 298 | if (result.IsError()) { | ||
| 299 | return result; | ||
| 300 | } | ||
| 301 | |||
| 302 | if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) { | ||
| 303 | return ResultInvalidTagType; | ||
| 304 | } | ||
| 305 | |||
| 306 | if (parameters.size() == 0) { | ||
| 307 | return ResultInvalidArgument; | ||
| 308 | } | ||
| 309 | |||
| 310 | const auto unknown = parameters[0].sector_key.unknown; | ||
| 311 | for (std::size_t i = 0; i < parameters.size(); i++) { | ||
| 312 | if (unknown != parameters[i].sector_key.unknown) { | ||
| 313 | return ResultInvalidArgument; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 282 | for (std::size_t i = 0; i < parameters.size(); i++) { | 317 | for (std::size_t i = 0; i < parameters.size(); i++) { |
| 283 | result = ReadMifare(parameters[i], read_block_data[i]); | 318 | result = ReadMifare(parameters[i], read_block_data[i]); |
| 284 | if (result.IsError()) { | 319 | if (result.IsError()) { |
| @@ -293,17 +328,8 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, | |||
| 293 | MifareReadBlockData& read_block_data) const { | 328 | MifareReadBlockData& read_block_data) const { |
| 294 | const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); | 329 | const std::size_t sector_index = parameter.sector_number * sizeof(DataBlock); |
| 295 | read_block_data.sector_number = parameter.sector_number; | 330 | read_block_data.sector_number = parameter.sector_number; |
| 296 | |||
| 297 | if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { | ||
| 298 | LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); | ||
| 299 | if (device_state == DeviceState::TagRemoved) { | ||
| 300 | return ResultTagRemoved; | ||
| 301 | } | ||
| 302 | return ResultWrongDeviceState; | ||
| 303 | } | ||
| 304 | |||
| 305 | if (mifare_data.size() < sector_index + sizeof(DataBlock)) { | 331 | if (mifare_data.size() < sector_index + sizeof(DataBlock)) { |
| 306 | return Mifare::ResultReadError; | 332 | return ResultMifareError288; |
| 307 | } | 333 | } |
| 308 | 334 | ||
| 309 | // TODO: Use parameter.sector_key to read encrypted data | 335 | // TODO: Use parameter.sector_key to read encrypted data |
| @@ -315,6 +341,28 @@ Result NfcDevice::ReadMifare(const MifareReadBlockParameter& parameter, | |||
| 315 | Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) { | 341 | Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) { |
| 316 | Result result = ResultSuccess; | 342 | Result result = ResultSuccess; |
| 317 | 343 | ||
| 344 | TagInfo tag_info{}; | ||
| 345 | result = GetTagInfo(tag_info, true); | ||
| 346 | |||
| 347 | if (result.IsError()) { | ||
| 348 | return result; | ||
| 349 | } | ||
| 350 | |||
| 351 | if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) { | ||
| 352 | return ResultInvalidTagType; | ||
| 353 | } | ||
| 354 | |||
| 355 | if (parameters.size() == 0) { | ||
| 356 | return ResultInvalidArgument; | ||
| 357 | } | ||
| 358 | |||
| 359 | const auto unknown = parameters[0].sector_key.unknown; | ||
| 360 | for (std::size_t i = 0; i < parameters.size(); i++) { | ||
| 361 | if (unknown != parameters[i].sector_key.unknown) { | ||
| 362 | return ResultInvalidArgument; | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 318 | for (std::size_t i = 0; i < parameters.size(); i++) { | 366 | for (std::size_t i = 0; i < parameters.size(); i++) { |
| 319 | result = WriteMifare(parameters[i]); | 367 | result = WriteMifare(parameters[i]); |
| 320 | if (result.IsError()) { | 368 | if (result.IsError()) { |
| @@ -324,7 +372,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet | |||
| 324 | 372 | ||
| 325 | if (!npad_device->WriteNfc(mifare_data)) { | 373 | if (!npad_device->WriteNfc(mifare_data)) { |
| 326 | LOG_ERROR(Service_NFP, "Error writing to file"); | 374 | LOG_ERROR(Service_NFP, "Error writing to file"); |
| 327 | return Mifare::ResultReadError; | 375 | return ResultMifareError288; |
| 328 | } | 376 | } |
| 329 | 377 | ||
| 330 | return result; | 378 | return result; |
| @@ -342,7 +390,7 @@ Result NfcDevice::WriteMifare(const MifareWriteBlockParameter& parameter) { | |||
| 342 | } | 390 | } |
| 343 | 391 | ||
| 344 | if (mifare_data.size() < sector_index + sizeof(DataBlock)) { | 392 | if (mifare_data.size() < sector_index + sizeof(DataBlock)) { |
| 345 | return Mifare::ResultReadError; | 393 | return ResultMifareError288; |
| 346 | } | 394 | } |
| 347 | 395 | ||
| 348 | // TODO: Use parameter.sector_key to encrypt the data | 396 | // TODO: Use parameter.sector_key to encrypt the data |
| @@ -366,7 +414,7 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target | |||
| 366 | 414 | ||
| 367 | if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { | 415 | if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { |
| 368 | LOG_ERROR(Service_NFP, "Not an amiibo"); | 416 | LOG_ERROR(Service_NFP, "Not an amiibo"); |
| 369 | return ResultNotAnAmiibo; | 417 | return ResultInvalidTagType; |
| 370 | } | 418 | } |
| 371 | 419 | ||
| 372 | // The loaded amiibo is not encrypted | 420 | // The loaded amiibo is not encrypted |
| @@ -381,14 +429,14 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target | |||
| 381 | } | 429 | } |
| 382 | 430 | ||
| 383 | if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | 431 | if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { |
| 384 | bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); | 432 | bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); |
| 385 | LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); | 433 | LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); |
| 386 | return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; | 434 | return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; |
| 387 | } | 435 | } |
| 388 | 436 | ||
| 389 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); | 437 | std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); |
| 390 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | 438 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); |
| 391 | WriteBackupData(encrypted_tag_data.uuid.uid, data); | 439 | WriteBackupData(encrypted_tag_data.uuid, data); |
| 392 | 440 | ||
| 393 | device_state = DeviceState::TagMounted; | 441 | device_state = DeviceState::TagMounted; |
| 394 | mount_target = mount_target_; | 442 | mount_target = mount_target_; |
| @@ -492,7 +540,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { | |||
| 492 | } | 540 | } |
| 493 | 541 | ||
| 494 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | 542 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); |
| 495 | WriteBackupData(encrypted_tag_data.uuid.uid, data); | 543 | WriteBackupData(encrypted_tag_data.uuid, data); |
| 496 | } | 544 | } |
| 497 | 545 | ||
| 498 | if (!npad_device->WriteNfc(data)) { | 546 | if (!npad_device->WriteNfc(data)) { |
| @@ -520,7 +568,7 @@ Result NfcDevice::Restore() { | |||
| 520 | return result; | 568 | return result; |
| 521 | } | 569 | } |
| 522 | 570 | ||
| 523 | result = ReadBackupData(tag_info.uuid, data); | 571 | result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data); |
| 524 | 572 | ||
| 525 | if (result.IsError()) { | 573 | if (result.IsError()) { |
| 526 | return result; | 574 | return result; |
| @@ -548,7 +596,7 @@ Result NfcDevice::Restore() { | |||
| 548 | } | 596 | } |
| 549 | 597 | ||
| 550 | if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { | 598 | if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) { |
| 551 | return ResultNotAnAmiibo; | 599 | return ResultInvalidTagType; |
| 552 | } | 600 | } |
| 553 | 601 | ||
| 554 | if (!is_plain_amiibo) { | 602 | if (!is_plain_amiibo) { |
| @@ -1194,10 +1242,12 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) { | |||
| 1194 | return FlushWithBreak(break_type); | 1242 | return FlushWithBreak(break_type); |
| 1195 | } | 1243 | } |
| 1196 | 1244 | ||
| 1197 | Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { | 1245 | Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const { |
| 1246 | ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); | ||
| 1198 | constexpr auto backup_dir = "backup"; | 1247 | constexpr auto backup_dir = "backup"; |
| 1199 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | 1248 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); |
| 1200 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | 1249 | const auto file_name = |
| 1250 | fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); | ||
| 1201 | 1251 | ||
| 1202 | if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { | 1252 | if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { |
| 1203 | return ResultUnableToAccessBackupFile; | 1253 | return ResultUnableToAccessBackupFile; |
| @@ -1206,10 +1256,19 @@ Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { | |||
| 1206 | return ResultSuccess; | 1256 | return ResultSuccess; |
| 1207 | } | 1257 | } |
| 1208 | 1258 | ||
| 1209 | Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { | 1259 | Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const { |
| 1260 | UniqueSerialNumber uuid{}; | ||
| 1261 | memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); | ||
| 1262 | return HasBackup(uuid, sizeof(NFP::TagUuid)); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, | ||
| 1266 | std::span<u8> data) const { | ||
| 1267 | ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); | ||
| 1210 | constexpr auto backup_dir = "backup"; | 1268 | constexpr auto backup_dir = "backup"; |
| 1211 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | 1269 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); |
| 1212 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | 1270 | const auto file_name = |
| 1271 | fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); | ||
| 1213 | 1272 | ||
| 1214 | const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, | 1273 | const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name, |
| 1215 | Common::FS::FileAccessMode::Read, | 1274 | Common::FS::FileAccessMode::Read, |
| @@ -1228,12 +1287,21 @@ Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u | |||
| 1228 | return ResultSuccess; | 1287 | return ResultSuccess; |
| 1229 | } | 1288 | } |
| 1230 | 1289 | ||
| 1231 | Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { | 1290 | Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const { |
| 1291 | UniqueSerialNumber uuid{}; | ||
| 1292 | memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); | ||
| 1293 | return ReadBackupData(uuid, sizeof(NFP::TagUuid), data); | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, | ||
| 1297 | std::span<const u8> data) { | ||
| 1298 | ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size"); | ||
| 1232 | constexpr auto backup_dir = "backup"; | 1299 | constexpr auto backup_dir = "backup"; |
| 1233 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); | 1300 | const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir); |
| 1234 | const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, "")); | 1301 | const auto file_name = |
| 1302 | fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, "")); | ||
| 1235 | 1303 | ||
| 1236 | if (HasBackup(uid).IsError()) { | 1304 | if (HasBackup(uid, uuid_size).IsError()) { |
| 1237 | if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { | 1305 | if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) { |
| 1238 | return ResultBackupPathAlreadyExist; | 1306 | return ResultBackupPathAlreadyExist; |
| 1239 | } | 1307 | } |
| @@ -1260,6 +1328,12 @@ Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span< | |||
| 1260 | return ResultSuccess; | 1328 | return ResultSuccess; |
| 1261 | } | 1329 | } |
| 1262 | 1330 | ||
| 1331 | Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) { | ||
| 1332 | UniqueSerialNumber uuid{}; | ||
| 1333 | memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid)); | ||
| 1334 | return WriteBackupData(uuid, sizeof(NFP::TagUuid), data); | ||
| 1335 | } | ||
| 1336 | |||
| 1263 | Result NfcDevice::WriteNtf(std::span<const u8> data) { | 1337 | Result NfcDevice::WriteNtf(std::span<const u8> data) { |
| 1264 | if (device_state != DeviceState::TagMounted) { | 1338 | if (device_state != DeviceState::TagMounted) { |
| 1265 | LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); | 1339 | LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); |
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 6f049b687..7560210d6 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h | |||
| @@ -86,9 +86,14 @@ public: | |||
| 86 | Result GetAll(NFP::NfpData& data) const; | 86 | Result GetAll(NFP::NfpData& data) const; |
| 87 | Result SetAll(const NFP::NfpData& data); | 87 | Result SetAll(const NFP::NfpData& data); |
| 88 | Result BreakTag(NFP::BreakType break_type); | 88 | Result BreakTag(NFP::BreakType break_type); |
| 89 | Result HasBackup(const NFC::UniqueSerialNumber& uid) const; | 89 | Result HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const; |
| 90 | Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; | 90 | Result HasBackup(const NFP::TagUuid& tag_uid) const; |
| 91 | Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data); | 91 | Result ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, |
| 92 | std::span<u8> data) const; | ||
| 93 | Result ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const; | ||
| 94 | Result WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size, | ||
| 95 | std::span<const u8> data); | ||
| 96 | Result WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data); | ||
| 92 | Result WriteNtf(std::span<const u8> data); | 97 | Result WriteNtf(std::span<const u8> data); |
| 93 | 98 | ||
| 94 | u64 GetHandle() const; | 99 | u64 GetHandle() const; |
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index cffd602df..b0456508e 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp | |||
| @@ -550,7 +550,7 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons | |||
| 550 | } | 550 | } |
| 551 | 551 | ||
| 552 | if (result.IsSuccess()) { | 552 | if (result.IsSuccess()) { |
| 553 | result = device->ReadBackupData(tag_info.uuid, data); | 553 | result = device->ReadBackupData(tag_info.uuid, tag_info.uuid_length, data); |
| 554 | result = VerifyDeviceResult(device, result); | 554 | result = VerifyDeviceResult(device, result); |
| 555 | } | 555 | } |
| 556 | 556 | ||
| @@ -569,7 +569,7 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat | |||
| 569 | } | 569 | } |
| 570 | 570 | ||
| 571 | if (result.IsSuccess()) { | 571 | if (result.IsSuccess()) { |
| 572 | result = device->WriteBackupData(tag_info.uuid, data); | 572 | result = device->WriteBackupData(tag_info.uuid, tag_info.uuid_length, data); |
| 573 | result = VerifyDeviceResult(device, result); | 573 | result = VerifyDeviceResult(device, result); |
| 574 | } | 574 | } |
| 575 | 575 | ||
diff --git a/src/core/hle/service/nfc/mifare_result.h b/src/core/hle/service/nfc/mifare_result.h index 4b60048a5..16a9171e6 100644 --- a/src/core/hle/service/nfc/mifare_result.h +++ b/src/core/hle/service/nfc/mifare_result.h | |||
| @@ -12,6 +12,6 @@ constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65); | |||
| 12 | constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); | 12 | constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); |
| 13 | constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); | 13 | constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); |
| 14 | constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); | 14 | constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); |
| 15 | constexpr Result ResultReadError(ErrorModule::NFCMifare, 288); | 15 | constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288); |
| 16 | 16 | ||
| 17 | } // namespace Service::NFC::Mifare | 17 | } // namespace Service::NFC::Mifare |
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 198d0f2b9..130fb7f78 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp | |||
| @@ -142,9 +142,13 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) { | |||
| 142 | void NfcInterface::StartDetection(HLERequestContext& ctx) { | 142 | void NfcInterface::StartDetection(HLERequestContext& ctx) { |
| 143 | IPC::RequestParser rp{ctx}; | 143 | IPC::RequestParser rp{ctx}; |
| 144 | const auto device_handle{rp.Pop<u64>()}; | 144 | const auto device_handle{rp.Pop<u64>()}; |
| 145 | const auto tag_protocol{rp.PopEnum<NfcProtocol>()}; | 145 | auto tag_protocol{NfcProtocol::All}; |
| 146 | LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); | 146 | |
| 147 | if (backend_type == BackendType::Nfc) { | ||
| 148 | tag_protocol = rp.PopEnum<NfcProtocol>(); | ||
| 149 | } | ||
| 147 | 150 | ||
| 151 | LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol); | ||
| 148 | auto result = GetManager()->StartDetection(device_handle, tag_protocol); | 152 | auto result = GetManager()->StartDetection(device_handle, tag_protocol); |
| 149 | result = TranslateResultToServiceError(result); | 153 | result = TranslateResultToServiceError(result); |
| 150 | 154 | ||
| @@ -355,7 +359,7 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { | |||
| 355 | if (result == ResultApplicationAreaExist) { | 359 | if (result == ResultApplicationAreaExist) { |
| 356 | return NFP::ResultApplicationAreaExist; | 360 | return NFP::ResultApplicationAreaExist; |
| 357 | } | 361 | } |
| 358 | if (result == ResultNotAnAmiibo) { | 362 | if (result == ResultInvalidTagType) { |
| 359 | return NFP::ResultNotAnAmiibo; | 363 | return NFP::ResultNotAnAmiibo; |
| 360 | } | 364 | } |
| 361 | if (result == ResultUnableToAccessBackupFile) { | 365 | if (result == ResultUnableToAccessBackupFile) { |
| @@ -381,6 +385,9 @@ Result NfcInterface::TranslateResultToMifare(Result result) const { | |||
| 381 | if (result == ResultTagRemoved) { | 385 | if (result == ResultTagRemoved) { |
| 382 | return Mifare::ResultTagRemoved; | 386 | return Mifare::ResultTagRemoved; |
| 383 | } | 387 | } |
| 388 | if (result == ResultInvalidTagType) { | ||
| 389 | return Mifare::ResultNotAMifare; | ||
| 390 | } | ||
| 384 | LOG_WARNING(Service_NFC, "Result conversion not handled"); | 391 | LOG_WARNING(Service_NFC, "Result conversion not handled"); |
| 385 | return result; | 392 | return result; |
| 386 | } | 393 | } |
diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 59a808740..715c0e80c 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h | |||
| @@ -24,7 +24,8 @@ constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); | |||
| 24 | constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); | 24 | constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); |
| 25 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); | 25 | constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); |
| 26 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); | 26 | constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); |
| 27 | constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); | 27 | constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178); |
| 28 | constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); | 28 | constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); |
| 29 | constexpr Result ResultMifareError288(ErrorModule::NFC, 288); | ||
| 29 | 30 | ||
| 30 | } // namespace Service::NFC | 31 | } // namespace Service::NFC |
diff --git a/src/core/hle/service/nfc/nfc_types.h b/src/core/hle/service/nfc/nfc_types.h index c7ebd1fdb..68e724442 100644 --- a/src/core/hle/service/nfc/nfc_types.h +++ b/src/core/hle/service/nfc/nfc_types.h | |||
| @@ -35,32 +35,35 @@ enum class State : u32 { | |||
| 35 | 35 | ||
| 36 | // This is nn::nfc::TagType | 36 | // This is nn::nfc::TagType |
| 37 | enum class TagType : u32 { | 37 | enum class TagType : u32 { |
| 38 | None, | 38 | None = 0, |
| 39 | Type1, // ISO14443A RW 96-2k bytes 106kbit/s | 39 | Type1 = 1U << 0, // ISO14443A RW. Topaz |
| 40 | Type2, // ISO14443A RW/RO 540 bytes 106kbit/s | 40 | Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN |
| 41 | Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s | 41 | Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa |
| 42 | Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s | 42 | Type4A = 1U << 3, // ISO14443A RW/RO. DESFire |
| 43 | Type5, // ISO15693 RW/RO 540 bytes 106kbit/s | 43 | Type4B = 1U << 4, // ISO14443B RW/RO. DESFire |
| 44 | Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV | ||
| 45 | Mifare = 1U << 6, // Mifare classic. Skylanders | ||
| 46 | All = 0xFFFFFFFF, | ||
| 44 | }; | 47 | }; |
| 45 | 48 | ||
| 46 | enum class PackedTagType : u8 { | 49 | enum class PackedTagType : u8 { |
| 47 | None, | 50 | None = 0, |
| 48 | Type1, // ISO14443A RW 96-2k bytes 106kbit/s | 51 | Type1 = 1U << 0, // ISO14443A RW. Topaz |
| 49 | Type2, // ISO14443A RW/RO 540 bytes 106kbit/s | 52 | Type2 = 1U << 1, // ISO14443A RW. Ultralight, NTAGX, ST25TN |
| 50 | Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s | 53 | Type3 = 1U << 2, // ISO14443A RW/RO. Sony FeliCa |
| 51 | Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s | 54 | Type4A = 1U << 3, // ISO14443A RW/RO. DESFire |
| 52 | Type5, // ISO15693 RW/RO 540 bytes 106kbit/s | 55 | Type4B = 1U << 4, // ISO14443B RW/RO. DESFire |
| 56 | Type5 = 1U << 5, // ISO15693 RW/RO. SLI, SLIX, ST25TV | ||
| 57 | Mifare = 1U << 6, // Mifare classic. Skylanders | ||
| 58 | All = 0xFF, | ||
| 53 | }; | 59 | }; |
| 54 | 60 | ||
| 55 | // This is nn::nfc::NfcProtocol | 61 | // This is nn::nfc::NfcProtocol |
| 56 | // Verify this enum. It might be completely wrong default protocol is 0x48 | ||
| 57 | enum class NfcProtocol : u32 { | 62 | enum class NfcProtocol : u32 { |
| 58 | None, | 63 | None, |
| 59 | TypeA = 1U << 0, // ISO14443A | 64 | TypeA = 1U << 0, // ISO14443A |
| 60 | TypeB = 1U << 1, // ISO14443B | 65 | TypeB = 1U << 1, // ISO14443B |
| 61 | TypeF = 1U << 2, // Sony FeliCa | 66 | TypeF = 1U << 2, // Sony FeliCa |
| 62 | Unknown1 = 1U << 3, | ||
| 63 | Unknown2 = 1U << 5, | ||
| 64 | All = 0xFFFFFFFFU, | 67 | All = 0xFFFFFFFFU, |
| 65 | }; | 68 | }; |
| 66 | 69 | ||
| @@ -69,8 +72,7 @@ enum class TestWaveType : u32 { | |||
| 69 | Unknown, | 72 | Unknown, |
| 70 | }; | 73 | }; |
| 71 | 74 | ||
| 72 | using UniqueSerialNumber = std::array<u8, 7>; | 75 | using UniqueSerialNumber = std::array<u8, 10>; |
| 73 | using UniqueSerialNumberExtension = std::array<u8, 3>; | ||
| 74 | 76 | ||
| 75 | // This is nn::nfc::DeviceHandle | 77 | // This is nn::nfc::DeviceHandle |
| 76 | using DeviceHandle = u64; | 78 | using DeviceHandle = u64; |
| @@ -78,7 +80,6 @@ using DeviceHandle = u64; | |||
| 78 | // This is nn::nfc::TagInfo | 80 | // This is nn::nfc::TagInfo |
| 79 | struct TagInfo { | 81 | struct TagInfo { |
| 80 | UniqueSerialNumber uuid; | 82 | UniqueSerialNumber uuid; |
| 81 | UniqueSerialNumberExtension uuid_extension; | ||
| 82 | u8 uuid_length; | 83 | u8 uuid_length; |
| 83 | INSERT_PADDING_BYTES(0x15); | 84 | INSERT_PADDING_BYTES(0x15); |
| 84 | NfcProtocol protocol; | 85 | NfcProtocol protocol; |
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 7d36d5ee6..aed12a7f8 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h | |||
| @@ -85,7 +85,7 @@ enum class CabinetMode : u8 { | |||
| 85 | StartFormatter, | 85 | StartFormatter, |
| 86 | }; | 86 | }; |
| 87 | 87 | ||
| 88 | using LockBytes = std::array<u8, 2>; | 88 | using UuidPart = std::array<u8, 3>; |
| 89 | using HashData = std::array<u8, 0x20>; | 89 | using HashData = std::array<u8, 0x20>; |
| 90 | using ApplicationArea = std::array<u8, 0xD8>; | 90 | using ApplicationArea = std::array<u8, 0xD8>; |
| 91 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | 91 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; |
| @@ -93,12 +93,20 @@ using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | |||
| 93 | // This is nn::nfp::TagInfo | 93 | // This is nn::nfp::TagInfo |
| 94 | using TagInfo = NFC::TagInfo; | 94 | using TagInfo = NFC::TagInfo; |
| 95 | 95 | ||
| 96 | struct NtagTagUuid { | ||
| 97 | UuidPart part1; | ||
| 98 | UuidPart part2; | ||
| 99 | u8 nintendo_id; | ||
| 100 | }; | ||
| 101 | static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size"); | ||
| 102 | |||
| 96 | struct TagUuid { | 103 | struct TagUuid { |
| 97 | NFC::UniqueSerialNumber uid; | 104 | UuidPart part1; |
| 105 | u8 crc_check1; | ||
| 106 | UuidPart part2; | ||
| 98 | u8 nintendo_id; | 107 | u8 nintendo_id; |
| 99 | LockBytes lock_bytes; | ||
| 100 | }; | 108 | }; |
| 101 | static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); | 109 | static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size"); |
| 102 | 110 | ||
| 103 | struct WriteDate { | 111 | struct WriteDate { |
| 104 | u16 year; | 112 | u16 year; |
| @@ -231,7 +239,8 @@ struct EncryptedAmiiboFile { | |||
| 231 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | 239 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); |
| 232 | 240 | ||
| 233 | struct NTAG215File { | 241 | struct NTAG215File { |
| 234 | LockBytes lock_bytes; // Tag UUID | 242 | u8 uid_crc_check2; |
| 243 | u8 internal_number; | ||
| 235 | u16 static_lock; // Set defined pages as read only | 244 | u16 static_lock; // Set defined pages as read only |
| 236 | u32 compability_container; // Defines available memory | 245 | u32 compability_container; // Defines available memory |
| 237 | HashData hmac_data; // Hash | 246 | HashData hmac_data; // Hash |
| @@ -250,8 +259,7 @@ struct NTAG215File { | |||
| 250 | u32_be register_info_crc; | 259 | u32_be register_info_crc; |
| 251 | ApplicationArea application_area; // Encrypted Game data | 260 | ApplicationArea application_area; // Encrypted Game data |
| 252 | HashData hmac_tag; // Hash | 261 | HashData hmac_tag; // Hash |
| 253 | NFC::UniqueSerialNumber uid; // Unique serial number | 262 | TagUuid uid; |
| 254 | u8 nintendo_id; // Tag UUID | ||
| 255 | AmiiboModelInfo model_info; | 263 | AmiiboModelInfo model_info; |
| 256 | HashData keygen_salt; // Salt | 264 | HashData keygen_salt; // Salt |
| 257 | u32 dynamic_lock; // Dynamic lock | 265 | u32 dynamic_lock; // Dynamic lock |
| @@ -264,7 +272,9 @@ static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be tr | |||
| 264 | #pragma pack() | 272 | #pragma pack() |
| 265 | 273 | ||
| 266 | struct EncryptedNTAG215File { | 274 | struct EncryptedNTAG215File { |
| 267 | TagUuid uuid; // Unique serial number | 275 | TagUuid uuid; |
| 276 | u8 uuid_crc_check2; | ||
| 277 | u8 internal_number; | ||
| 268 | u16 static_lock; // Set defined pages as read only | 278 | u16 static_lock; // Set defined pages as read only |
| 269 | u32 compability_container; // Defines available memory | 279 | u32 compability_container; // Defines available memory |
| 270 | EncryptedAmiiboFile user_memory; // Writable data | 280 | EncryptedAmiiboFile user_memory; // Writable data |
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index e1728c06d..63aacd19f 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp | |||
| @@ -849,8 +849,9 @@ static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& | |||
| 849 | static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, | 849 | static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, |
| 850 | CalendarTimeInternal& calendar_time, | 850 | CalendarTimeInternal& calendar_time, |
| 851 | CalendarAdditionalInfo& calendar_additional_info) { | 851 | CalendarAdditionalInfo& calendar_additional_info) { |
| 852 | if ((rules.go_ahead && time < rules.ats[0]) || | 852 | ASSERT(rules.go_ahead ? rules.time_count > 0 : true); |
| 853 | (rules.go_back && time > rules.ats[rules.time_count - 1])) { | 853 | if ((rules.go_back && time < rules.ats[0]) || |
| 854 | (rules.go_ahead && time > rules.ats[rules.time_count - 1])) { | ||
| 854 | s64 seconds{}; | 855 | s64 seconds{}; |
| 855 | if (time < rules.ats[0]) { | 856 | if (time < rules.ats[0]) { |
| 856 | seconds = rules.ats[0] - time; | 857 | seconds = rules.ats[0] - time; |
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index e8273e152..8171c82a5 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp | |||
| @@ -112,20 +112,14 @@ void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { | |||
| 112 | LOG_DEBUG(Service_Time, "called, location_name={}", location_name); | 112 | LOG_DEBUG(Service_Time, "called, location_name={}", location_name); |
| 113 | 113 | ||
| 114 | TimeZone::TimeZoneRule time_zone_rule{}; | 114 | TimeZone::TimeZoneRule time_zone_rule{}; |
| 115 | if (const Result result{ | 115 | const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; |
| 116 | time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; | ||
| 117 | result != ResultSuccess) { | ||
| 118 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 119 | rb.Push(result); | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | 116 | ||
| 123 | std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); | 117 | std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); |
| 124 | std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); | 118 | std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); |
| 125 | ctx.WriteBuffer(time_zone_rule_outbuffer); | 119 | ctx.WriteBuffer(time_zone_rule_outbuffer); |
| 126 | 120 | ||
| 127 | IPC::ResponseBuilder rb{ctx, 2}; | 121 | IPC::ResponseBuilder rb{ctx, 2}; |
| 128 | rb.Push(ResultSuccess); | 122 | rb.Push(result); |
| 129 | } | 123 | } |
| 130 | 124 | ||
| 131 | void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { | 125 | void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9bafd8cc0..45977d578 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -719,9 +719,15 @@ void BufferCache<P>::BindHostVertexBuffers() { | |||
| 719 | bool any_valid{false}; | 719 | bool any_valid{false}; |
| 720 | auto& flags = maxwell3d->dirty.flags; | 720 | auto& flags = maxwell3d->dirty.flags; |
| 721 | for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { | 721 | for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { |
| 722 | const Binding& binding = channel_state->vertex_buffers[index]; | ||
| 723 | Buffer& buffer = slot_buffers[binding.buffer_id]; | ||
| 724 | TouchBuffer(buffer, binding.buffer_id); | ||
| 725 | SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); | ||
| 722 | if (!flags[Dirty::VertexBuffer0 + index]) { | 726 | if (!flags[Dirty::VertexBuffer0 + index]) { |
| 723 | continue; | 727 | continue; |
| 724 | } | 728 | } |
| 729 | flags[Dirty::VertexBuffer0 + index] = false; | ||
| 730 | |||
| 725 | host_bindings.min_index = std::min(host_bindings.min_index, index); | 731 | host_bindings.min_index = std::min(host_bindings.min_index, index); |
| 726 | host_bindings.max_index = std::max(host_bindings.max_index, index); | 732 | host_bindings.max_index = std::max(host_bindings.max_index, index); |
| 727 | any_valid = true; | 733 | any_valid = true; |
| @@ -735,9 +741,6 @@ void BufferCache<P>::BindHostVertexBuffers() { | |||
| 735 | const Binding& binding = channel_state->vertex_buffers[index]; | 741 | const Binding& binding = channel_state->vertex_buffers[index]; |
| 736 | Buffer& buffer = slot_buffers[binding.buffer_id]; | 742 | Buffer& buffer = slot_buffers[binding.buffer_id]; |
| 737 | 743 | ||
| 738 | TouchBuffer(buffer, binding.buffer_id); | ||
| 739 | SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); | ||
| 740 | |||
| 741 | const u32 stride = maxwell3d->regs.vertex_streams[index].stride; | 744 | const u32 stride = maxwell3d->regs.vertex_streams[index].stride; |
| 742 | const u32 offset = buffer.Offset(binding.cpu_addr); | 745 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 743 | 746 | ||
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp index 1a0cea9b7..3151c0db8 100644 --- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp | |||
| @@ -87,7 +87,8 @@ void ComputePipeline::Configure() { | |||
| 87 | texture_cache.SynchronizeComputeDescriptors(); | 87 | texture_cache.SynchronizeComputeDescriptors(); |
| 88 | 88 | ||
| 89 | boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; | 89 | boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; |
| 90 | std::array<GLuint, MAX_TEXTURES> samplers; | 90 | boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers; |
| 91 | std::array<GLuint, MAX_TEXTURES> gl_samplers; | ||
| 91 | std::array<GLuint, MAX_TEXTURES> textures; | 92 | std::array<GLuint, MAX_TEXTURES> textures; |
| 92 | std::array<GLuint, MAX_IMAGES> images; | 93 | std::array<GLuint, MAX_IMAGES> images; |
| 93 | GLsizei sampler_binding{}; | 94 | GLsizei sampler_binding{}; |
| @@ -131,7 +132,6 @@ void ComputePipeline::Configure() { | |||
| 131 | for (u32 index = 0; index < desc.count; ++index) { | 132 | for (u32 index = 0; index < desc.count; ++index) { |
| 132 | const auto handle{read_handle(desc, index)}; | 133 | const auto handle{read_handle(desc, index)}; |
| 133 | views.push_back({handle.first}); | 134 | views.push_back({handle.first}); |
| 134 | samplers[sampler_binding++] = 0; | ||
| 135 | } | 135 | } |
| 136 | } | 136 | } |
| 137 | for (const auto& desc : info.image_buffer_descriptors) { | 137 | for (const auto& desc : info.image_buffer_descriptors) { |
| @@ -142,8 +142,8 @@ void ComputePipeline::Configure() { | |||
| 142 | const auto handle{read_handle(desc, index)}; | 142 | const auto handle{read_handle(desc, index)}; |
| 143 | views.push_back({handle.first}); | 143 | views.push_back({handle.first}); |
| 144 | 144 | ||
| 145 | Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); | 145 | VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); |
| 146 | samplers[sampler_binding++] = sampler->Handle(); | 146 | samplers.push_back(sampler); |
| 147 | } | 147 | } |
| 148 | } | 148 | } |
| 149 | for (const auto& desc : info.image_descriptors) { | 149 | for (const auto& desc : info.image_descriptors) { |
| @@ -186,10 +186,17 @@ void ComputePipeline::Configure() { | |||
| 186 | 186 | ||
| 187 | const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers + | 187 | const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers + |
| 188 | num_image_buffers}; | 188 | num_image_buffers}; |
| 189 | const VideoCommon::SamplerId* samplers_it{samplers.data()}; | ||
| 189 | texture_binding += num_texture_buffers; | 190 | texture_binding += num_texture_buffers; |
| 190 | image_binding += num_image_buffers; | 191 | image_binding += num_image_buffers; |
| 191 | 192 | ||
| 192 | u32 texture_scaling_mask{}; | 193 | u32 texture_scaling_mask{}; |
| 194 | |||
| 195 | for (const auto& desc : info.texture_buffer_descriptors) { | ||
| 196 | for (u32 index = 0; index < desc.count; ++index) { | ||
| 197 | gl_samplers[sampler_binding++] = 0; | ||
| 198 | } | ||
| 199 | } | ||
| 193 | for (const auto& desc : info.texture_descriptors) { | 200 | for (const auto& desc : info.texture_descriptors) { |
| 194 | for (u32 index = 0; index < desc.count; ++index) { | 201 | for (u32 index = 0; index < desc.count; ++index) { |
| 195 | ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; | 202 | ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; |
| @@ -198,6 +205,12 @@ void ComputePipeline::Configure() { | |||
| 198 | texture_scaling_mask |= 1u << texture_binding; | 205 | texture_scaling_mask |= 1u << texture_binding; |
| 199 | } | 206 | } |
| 200 | ++texture_binding; | 207 | ++texture_binding; |
| 208 | |||
| 209 | const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))}; | ||
| 210 | const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && | ||
| 211 | !image_view.SupportsAnisotropy()}; | ||
| 212 | gl_samplers[sampler_binding++] = | ||
| 213 | use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle(); | ||
| 201 | } | 214 | } |
| 202 | } | 215 | } |
| 203 | u32 image_scaling_mask{}; | 216 | u32 image_scaling_mask{}; |
| @@ -228,7 +241,7 @@ void ComputePipeline::Configure() { | |||
| 228 | if (texture_binding != 0) { | 241 | if (texture_binding != 0) { |
| 229 | ASSERT(texture_binding == sampler_binding); | 242 | ASSERT(texture_binding == sampler_binding); |
| 230 | glBindTextures(0, texture_binding, textures.data()); | 243 | glBindTextures(0, texture_binding, textures.data()); |
| 231 | glBindSamplers(0, sampler_binding, samplers.data()); | 244 | glBindSamplers(0, sampler_binding, gl_samplers.data()); |
| 232 | } | 245 | } |
| 233 | if (image_binding != 0) { | 246 | if (image_binding != 0) { |
| 234 | glBindImageTextures(0, image_binding, images.data()); | 247 | glBindImageTextures(0, image_binding, images.data()); |
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 89000d6e0..c58f760b8 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp | |||
| @@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c | |||
| 275 | template <typename Spec> | 275 | template <typename Spec> |
| 276 | void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | 276 | void GraphicsPipeline::ConfigureImpl(bool is_indexed) { |
| 277 | std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; | 277 | std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; |
| 278 | std::array<GLuint, MAX_TEXTURES> samplers; | 278 | std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers; |
| 279 | size_t views_index{}; | 279 | size_t views_index{}; |
| 280 | GLsizei sampler_binding{}; | 280 | size_t samplers_index{}; |
| 281 | 281 | ||
| 282 | texture_cache.SynchronizeGraphicsDescriptors(); | 282 | texture_cache.SynchronizeGraphicsDescriptors(); |
| 283 | 283 | ||
| @@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 337 | for (u32 index = 0; index < desc.count; ++index) { | 337 | for (u32 index = 0; index < desc.count; ++index) { |
| 338 | const auto handle{read_handle(desc, index)}; | 338 | const auto handle{read_handle(desc, index)}; |
| 339 | views[views_index++] = {handle.first}; | 339 | views[views_index++] = {handle.first}; |
| 340 | samplers[sampler_binding++] = 0; | ||
| 341 | } | 340 | } |
| 342 | } | 341 | } |
| 343 | } | 342 | } |
| @@ -351,8 +350,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 351 | const auto handle{read_handle(desc, index)}; | 350 | const auto handle{read_handle(desc, index)}; |
| 352 | views[views_index++] = {handle.first}; | 351 | views[views_index++] = {handle.first}; |
| 353 | 352 | ||
| 354 | Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; | 353 | VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; |
| 355 | samplers[sampler_binding++] = sampler->Handle(); | 354 | samplers[samplers_index++] = sampler; |
| 356 | } | 355 | } |
| 357 | } | 356 | } |
| 358 | if constexpr (Spec::has_images) { | 357 | if constexpr (Spec::has_images) { |
| @@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 445 | program_manager.BindSourcePrograms(source_programs); | 444 | program_manager.BindSourcePrograms(source_programs); |
| 446 | } | 445 | } |
| 447 | const VideoCommon::ImageViewInOut* views_it{views.data()}; | 446 | const VideoCommon::ImageViewInOut* views_it{views.data()}; |
| 447 | const VideoCommon::SamplerId* samplers_it{samplers.data()}; | ||
| 448 | GLsizei texture_binding = 0; | 448 | GLsizei texture_binding = 0; |
| 449 | GLsizei image_binding = 0; | 449 | GLsizei image_binding = 0; |
| 450 | GLsizei sampler_binding{}; | ||
| 450 | std::array<GLuint, MAX_TEXTURES> textures; | 451 | std::array<GLuint, MAX_TEXTURES> textures; |
| 451 | std::array<GLuint, MAX_IMAGES> images; | 452 | std::array<GLuint, MAX_IMAGES> images; |
| 453 | std::array<GLuint, MAX_TEXTURES> gl_samplers; | ||
| 452 | const auto prepare_stage{[&](size_t stage) { | 454 | const auto prepare_stage{[&](size_t stage) { |
| 453 | buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); | 455 | buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); |
| 454 | buffer_cache.BindHostStageBuffers(stage); | 456 | buffer_cache.BindHostStageBuffers(stage); |
| @@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 465 | u32 stage_image_binding{}; | 467 | u32 stage_image_binding{}; |
| 466 | 468 | ||
| 467 | const auto& info{stage_infos[stage]}; | 469 | const auto& info{stage_infos[stage]}; |
| 470 | if constexpr (Spec::has_texture_buffers) { | ||
| 471 | for (const auto& desc : info.texture_buffer_descriptors) { | ||
| 472 | for (u32 index = 0; index < desc.count; ++index) { | ||
| 473 | gl_samplers[sampler_binding++] = 0; | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 468 | for (const auto& desc : info.texture_descriptors) { | 477 | for (const auto& desc : info.texture_descriptors) { |
| 469 | for (u32 index = 0; index < desc.count; ++index) { | 478 | for (u32 index = 0; index < desc.count; ++index) { |
| 470 | ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; | 479 | ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; |
| @@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 474 | } | 483 | } |
| 475 | ++texture_binding; | 484 | ++texture_binding; |
| 476 | ++stage_texture_binding; | 485 | ++stage_texture_binding; |
| 486 | |||
| 487 | const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))}; | ||
| 488 | const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && | ||
| 489 | !image_view.SupportsAnisotropy()}; | ||
| 490 | gl_samplers[sampler_binding++] = | ||
| 491 | use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle(); | ||
| 477 | } | 492 | } |
| 478 | } | 493 | } |
| 479 | for (const auto& desc : info.image_descriptors) { | 494 | for (const auto& desc : info.image_descriptors) { |
| @@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 534 | if (texture_binding != 0) { | 549 | if (texture_binding != 0) { |
| 535 | ASSERT(texture_binding == sampler_binding); | 550 | ASSERT(texture_binding == sampler_binding); |
| 536 | glBindTextures(0, texture_binding, textures.data()); | 551 | glBindTextures(0, texture_binding, textures.data()); |
| 537 | glBindSamplers(0, sampler_binding, samplers.data()); | 552 | glBindSamplers(0, sampler_binding, gl_samplers.data()); |
| 538 | } | 553 | } |
| 539 | if (image_binding != 0) { | 554 | if (image_binding != 0) { |
| 540 | glBindImageTextures(0, image_binding, images.data()); | 555 | glBindImageTextures(0, image_binding, images.data()); |
diff --git a/src/video_core/renderer_opengl/gl_shader_context.h b/src/video_core/renderer_opengl/gl_shader_context.h index 207a75d42..d12cd06fa 100644 --- a/src/video_core/renderer_opengl/gl_shader_context.h +++ b/src/video_core/renderer_opengl/gl_shader_context.h | |||
| @@ -16,9 +16,9 @@ struct ShaderPools { | |||
| 16 | inst.ReleaseContents(); | 16 | inst.ReleaseContents(); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | Shader::ObjectPool<Shader::IR::Inst> inst; | 19 | Shader::ObjectPool<Shader::IR::Inst> inst{8192}; |
| 20 | Shader::ObjectPool<Shader::IR::Block> block; | 20 | Shader::ObjectPool<Shader::IR::Block> block{32}; |
| 21 | Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; | 21 | Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32}; |
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | struct Context { | 24 | struct Context { |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 1c5dbcdd8..3b446be07 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) { | |||
| 1268 | 1268 | ||
| 1269 | UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); | 1269 | UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); |
| 1270 | 1270 | ||
| 1271 | sampler.Create(); | 1271 | const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); |
| 1272 | const GLuint handle = sampler.handle; | 1272 | |
| 1273 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); | 1273 | const auto create_sampler = [&](const f32 anisotropy) { |
| 1274 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); | 1274 | OGLSampler new_sampler; |
| 1275 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); | 1275 | new_sampler.Create(); |
| 1276 | glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); | 1276 | const GLuint handle = new_sampler.handle; |
| 1277 | glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); | 1277 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); |
| 1278 | glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); | 1278 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); |
| 1279 | glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); | 1279 | glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); |
| 1280 | glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); | 1280 | glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); |
| 1281 | glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); | 1281 | glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); |
| 1282 | glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); | 1282 | glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); |
| 1283 | glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); | 1283 | glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); |
| 1284 | 1284 | glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); | |
| 1285 | if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { | 1285 | glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); |
| 1286 | const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); | 1286 | glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); |
| 1287 | glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); | 1287 | glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); |
| 1288 | } else { | 1288 | |
| 1289 | LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); | 1289 | if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { |
| 1290 | } | 1290 | glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy); |
| 1291 | if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { | 1291 | } else { |
| 1292 | glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); | 1292 | LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); |
| 1293 | } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { | 1293 | } |
| 1294 | LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); | 1294 | if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { |
| 1295 | } | 1295 | glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); |
| 1296 | if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { | 1296 | } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { |
| 1297 | glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); | 1297 | LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); |
| 1298 | } else if (seamless == GL_FALSE) { | 1298 | } |
| 1299 | // We default to false because it's more common | 1299 | if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { |
| 1300 | LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); | 1300 | glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); |
| 1301 | } else if (seamless == GL_FALSE) { | ||
| 1302 | // We default to false because it's more common | ||
| 1303 | LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); | ||
| 1304 | } | ||
| 1305 | return new_sampler; | ||
| 1306 | }; | ||
| 1307 | |||
| 1308 | sampler = create_sampler(max_anisotropy); | ||
| 1309 | |||
| 1310 | const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy); | ||
| 1311 | if (max_anisotropy > max_anisotropy_default) { | ||
| 1312 | sampler_default_anisotropy = create_sampler(max_anisotropy_default); | ||
| 1301 | } | 1313 | } |
| 1302 | } | 1314 | } |
| 1303 | 1315 | ||
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 1148b73d7..3676eaaa9 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -309,12 +309,21 @@ class Sampler { | |||
| 309 | public: | 309 | public: |
| 310 | explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); | 310 | explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); |
| 311 | 311 | ||
| 312 | GLuint Handle() const noexcept { | 312 | [[nodiscard]] GLuint Handle() const noexcept { |
| 313 | return sampler.handle; | 313 | return sampler.handle; |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | [[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept { | ||
| 317 | return sampler_default_anisotropy.handle; | ||
| 318 | } | ||
| 319 | |||
| 320 | [[nodiscard]] bool HasAddedAnisotropy() const noexcept { | ||
| 321 | return static_cast<bool>(sampler_default_anisotropy.handle); | ||
| 322 | } | ||
| 323 | |||
| 316 | private: | 324 | private: |
| 317 | OGLSampler sampler; | 325 | OGLSampler sampler; |
| 326 | OGLSampler sampler_default_anisotropy; | ||
| 318 | }; | 327 | }; |
| 319 | 328 | ||
| 320 | class Framebuffer { | 329 | class Framebuffer { |
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 983e1c2e1..71c783709 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h | |||
| @@ -178,7 +178,7 @@ public: | |||
| 178 | inline void PushImageDescriptors(TextureCache& texture_cache, | 178 | inline void PushImageDescriptors(TextureCache& texture_cache, |
| 179 | GuestDescriptorQueue& guest_descriptor_queue, | 179 | GuestDescriptorQueue& guest_descriptor_queue, |
| 180 | const Shader::Info& info, RescalingPushConstant& rescaling, | 180 | const Shader::Info& info, RescalingPushConstant& rescaling, |
| 181 | const VkSampler*& samplers, | 181 | const VideoCommon::SamplerId*& samplers, |
| 182 | const VideoCommon::ImageViewInOut*& views) { | 182 | const VideoCommon::ImageViewInOut*& views) { |
| 183 | const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); | 183 | const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); |
| 184 | const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); | 184 | const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); |
| @@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache, | |||
| 187 | for (const auto& desc : info.texture_descriptors) { | 187 | for (const auto& desc : info.texture_descriptors) { |
| 188 | for (u32 index = 0; index < desc.count; ++index) { | 188 | for (u32 index = 0; index < desc.count; ++index) { |
| 189 | const VideoCommon::ImageViewId image_view_id{(views++)->id}; | 189 | const VideoCommon::ImageViewId image_view_id{(views++)->id}; |
| 190 | const VkSampler sampler{*(samplers++)}; | 190 | const VideoCommon::SamplerId sampler_id{*(samplers++)}; |
| 191 | ImageView& image_view{texture_cache.GetImageView(image_view_id)}; | 191 | ImageView& image_view{texture_cache.GetImageView(image_view_id)}; |
| 192 | const VkImageView vk_image_view{image_view.Handle(desc.type)}; | 192 | const VkImageView vk_image_view{image_view.Handle(desc.type)}; |
| 193 | guest_descriptor_queue.AddSampledImage(vk_image_view, sampler); | 193 | const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; |
| 194 | const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && | ||
| 195 | !image_view.SupportsAnisotropy()}; | ||
| 196 | const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() | ||
| 197 | : sampler.Handle()}; | ||
| 198 | guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); | ||
| 194 | rescaling.PushTexture(texture_cache.IsRescaling(image_view)); | 199 | rescaling.PushTexture(texture_cache.IsRescaling(image_view)); |
| 195 | } | 200 | } |
| 196 | } | 201 | } |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 8c33722d3..e30fcb1ed 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -516,15 +516,15 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi | |||
| 516 | buffer_handles.push_back(handle); | 516 | buffer_handles.push_back(handle); |
| 517 | } | 517 | } |
| 518 | if (device.IsExtExtendedDynamicStateSupported()) { | 518 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 519 | scheduler.Record([bindings = bindings, | 519 | scheduler.Record([bindings = std::move(bindings), |
| 520 | buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { | 520 | buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { |
| 521 | cmdbuf.BindVertexBuffers2EXT( | 521 | cmdbuf.BindVertexBuffers2EXT( |
| 522 | bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), | 522 | bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), |
| 523 | bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); | 523 | bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); |
| 524 | }); | 524 | }); |
| 525 | } else { | 525 | } else { |
| 526 | scheduler.Record([bindings = bindings, | 526 | scheduler.Record([bindings = std::move(bindings), |
| 527 | buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { | 527 | buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { |
| 528 | cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, | 528 | cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, |
| 529 | buffer_handles.data(), bindings.offsets.data()); | 529 | buffer_handles.data(), bindings.offsets.data()); |
| 530 | }); | 530 | }); |
| @@ -561,12 +561,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings< | |||
| 561 | for (u32 index = 0; index < bindings.buffers.size(); ++index) { | 561 | for (u32 index = 0; index < bindings.buffers.size(); ++index) { |
| 562 | buffer_handles.push_back(bindings.buffers[index]->Handle()); | 562 | buffer_handles.push_back(bindings.buffers[index]->Handle()); |
| 563 | } | 563 | } |
| 564 | scheduler.Record( | 564 | scheduler.Record([bindings = std::move(bindings), |
| 565 | [bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { | 565 | buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { |
| 566 | cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), | 566 | cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), |
| 567 | buffer_handles.data(), bindings.offsets.data(), | 567 | buffer_handles.data(), bindings.offsets.data(), |
| 568 | bindings.sizes.data()); | 568 | bindings.sizes.data()); |
| 569 | }); | 569 | }); |
| 570 | } | 570 | } |
| 571 | 571 | ||
| 572 | void BufferCacheRuntime::ReserveNullBuffer() { | 572 | void BufferCacheRuntime::ReserveNullBuffer() { |
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 733e70d9d..73e585c2b 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp | |||
| @@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | |||
| 115 | 115 | ||
| 116 | static constexpr size_t max_elements = 64; | 116 | static constexpr size_t max_elements = 64; |
| 117 | boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; | 117 | boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; |
| 118 | boost::container::static_vector<VkSampler, max_elements> samplers; | 118 | boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers; |
| 119 | 119 | ||
| 120 | const auto& qmd{kepler_compute.launch_description}; | 120 | const auto& qmd{kepler_compute.launch_description}; |
| 121 | const auto& cbufs{qmd.const_buffer_config}; | 121 | const auto& cbufs{qmd.const_buffer_config}; |
| @@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | |||
| 160 | const auto handle{read_handle(desc, index)}; | 160 | const auto handle{read_handle(desc, index)}; |
| 161 | views.push_back({handle.first}); | 161 | views.push_back({handle.first}); |
| 162 | 162 | ||
| 163 | Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); | 163 | VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second); |
| 164 | samplers.push_back(sampler->Handle()); | 164 | samplers.push_back(sampler); |
| 165 | } | 165 | } |
| 166 | } | 166 | } |
| 167 | for (const auto& desc : info.image_descriptors) { | 167 | for (const auto& desc : info.image_descriptors) { |
| @@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | |||
| 192 | buffer_cache.BindHostComputeBuffers(); | 192 | buffer_cache.BindHostComputeBuffers(); |
| 193 | 193 | ||
| 194 | RescalingPushConstant rescaling; | 194 | RescalingPushConstant rescaling; |
| 195 | const VkSampler* samplers_it{samplers.data()}; | 195 | const VideoCommon::SamplerId* samplers_it{samplers.data()}; |
| 196 | const VideoCommon::ImageViewInOut* views_it{views.data()}; | 196 | const VideoCommon::ImageViewInOut* views_it{views.data()}; |
| 197 | PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, | 197 | PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, |
| 198 | views_it); | 198 | views_it); |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 506b78f08..c1595642e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { | |||
| 298 | template <typename Spec> | 298 | template <typename Spec> |
| 299 | void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | 299 | void GraphicsPipeline::ConfigureImpl(bool is_indexed) { |
| 300 | std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; | 300 | std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; |
| 301 | std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers; | 301 | std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers; |
| 302 | size_t sampler_index{}; | 302 | size_t sampler_index{}; |
| 303 | size_t view_index{}; | 303 | size_t view_index{}; |
| 304 | 304 | ||
| @@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 367 | const auto handle{read_handle(desc, index)}; | 367 | const auto handle{read_handle(desc, index)}; |
| 368 | views[view_index++] = {handle.first}; | 368 | views[view_index++] = {handle.first}; |
| 369 | 369 | ||
| 370 | Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; | 370 | VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)}; |
| 371 | samplers[sampler_index++] = sampler->Handle(); | 371 | samplers[sampler_index++] = sampler; |
| 372 | } | 372 | } |
| 373 | } | 373 | } |
| 374 | if constexpr (Spec::has_images) { | 374 | if constexpr (Spec::has_images) { |
| @@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | |||
| 453 | 453 | ||
| 454 | RescalingPushConstant rescaling; | 454 | RescalingPushConstant rescaling; |
| 455 | RenderAreaPushConstant render_area; | 455 | RenderAreaPushConstant render_area; |
| 456 | const VkSampler* samplers_it{samplers.data()}; | 456 | const VideoCommon::SamplerId* samplers_it{samplers.data()}; |
| 457 | const VideoCommon::ImageViewInOut* views_it{views.data()}; | 457 | const VideoCommon::ImageViewInOut* views_it{views.data()}; |
| 458 | const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { | 458 | const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { |
| 459 | buffer_cache.BindHostStageBuffers(stage); | 459 | buffer_cache.BindHostStageBuffers(stage); |
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index b128c4f6e..5eeda08d2 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <thread> | 4 | #include <thread> |
| 5 | 5 | ||
| 6 | #include "common/polyfill_ranges.h" | ||
| 6 | #include "common/settings.h" | 7 | #include "common/settings.h" |
| 7 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" | 8 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" |
| 8 | #include "video_core/vulkan_common/vulkan_device.h" | 9 | #include "video_core/vulkan_common/vulkan_device.h" |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 15aa7e224..e323ea0fd 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -92,9 +92,9 @@ struct ShaderPools { | |||
| 92 | inst.ReleaseContents(); | 92 | inst.ReleaseContents(); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | Shader::ObjectPool<Shader::IR::Inst> inst; | 95 | Shader::ObjectPool<Shader::IR::Inst> inst{8192}; |
| 96 | Shader::ObjectPool<Shader::IR::Block> block; | 96 | Shader::ObjectPool<Shader::IR::Block> block{32}; |
| 97 | Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block; | 97 | Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block{32}; |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
| 100 | class PipelineCache : public VideoCommon::ShaderCache { | 100 | class PipelineCache : public VideoCommon::ShaderCache { |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8711e2a87..f025f618b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1802,27 +1802,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t | |||
| 1802 | // Some games have samplers with garbage. Sanitize them here. | 1802 | // Some games have samplers with garbage. Sanitize them here. |
| 1803 | const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); | 1803 | const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); |
| 1804 | 1804 | ||
| 1805 | sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{ | 1805 | const auto create_sampler = [&](const f32 anisotropy) { |
| 1806 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | 1806 | return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ |
| 1807 | .pNext = pnext, | 1807 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| 1808 | .flags = 0, | 1808 | .pNext = pnext, |
| 1809 | .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), | 1809 | .flags = 0, |
| 1810 | .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), | 1810 | .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), |
| 1811 | .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), | 1811 | .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), |
| 1812 | .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), | 1812 | .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), |
| 1813 | .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), | 1813 | .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), |
| 1814 | .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), | 1814 | .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), |
| 1815 | .mipLodBias = tsc.LodBias(), | 1815 | .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), |
| 1816 | .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE), | 1816 | .mipLodBias = tsc.LodBias(), |
| 1817 | .maxAnisotropy = max_anisotropy, | 1817 | .anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), |
| 1818 | .compareEnable = tsc.depth_compare_enabled, | 1818 | .maxAnisotropy = anisotropy, |
| 1819 | .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), | 1819 | .compareEnable = tsc.depth_compare_enabled, |
| 1820 | .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), | 1820 | .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), |
| 1821 | .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), | 1821 | .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), |
| 1822 | .borderColor = | 1822 | .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), |
| 1823 | arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), | 1823 | .borderColor = |
| 1824 | .unnormalizedCoordinates = VK_FALSE, | 1824 | arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), |
| 1825 | }); | 1825 | .unnormalizedCoordinates = VK_FALSE, |
| 1826 | }); | ||
| 1827 | }; | ||
| 1828 | |||
| 1829 | sampler = create_sampler(max_anisotropy); | ||
| 1830 | |||
| 1831 | const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy); | ||
| 1832 | if (max_anisotropy > max_anisotropy_default) { | ||
| 1833 | sampler_default_anisotropy = create_sampler(max_anisotropy_default); | ||
| 1834 | } | ||
| 1826 | } | 1835 | } |
| 1827 | 1836 | ||
| 1828 | Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, | 1837 | Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 0f7a5ffd4..f14525dcb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -279,8 +279,17 @@ public: | |||
| 279 | return *sampler; | 279 | return *sampler; |
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | [[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept { | ||
| 283 | return *sampler_default_anisotropy; | ||
| 284 | } | ||
| 285 | |||
| 286 | [[nodiscard]] bool HasAddedAnisotropy() const noexcept { | ||
| 287 | return static_cast<bool>(sampler_default_anisotropy); | ||
| 288 | } | ||
| 289 | |||
| 282 | private: | 290 | private: |
| 283 | vk::Sampler sampler; | 291 | vk::Sampler sampler; |
| 292 | vk::Sampler sampler_default_anisotropy; | ||
| 284 | }; | 293 | }; |
| 285 | 294 | ||
| 286 | class Framebuffer { | 295 | class Framebuffer { |
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index d134b6738..0c5f4450d 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp | |||
| @@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in | |||
| 45 | 45 | ||
| 46 | ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} | 46 | ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} |
| 47 | 47 | ||
| 48 | bool ImageViewBase::SupportsAnisotropy() const noexcept { | ||
| 49 | const bool has_mips = range.extent.levels > 1; | ||
| 50 | const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray; | ||
| 51 | if (!has_mips || !is_2d) { | ||
| 52 | return false; | ||
| 53 | } | ||
| 54 | |||
| 55 | switch (format) { | ||
| 56 | case PixelFormat::R8_UNORM: | ||
| 57 | case PixelFormat::R8_SNORM: | ||
| 58 | case PixelFormat::R8_SINT: | ||
| 59 | case PixelFormat::R8_UINT: | ||
| 60 | case PixelFormat::BC4_UNORM: | ||
| 61 | case PixelFormat::BC4_SNORM: | ||
| 62 | case PixelFormat::BC5_UNORM: | ||
| 63 | case PixelFormat::BC5_SNORM: | ||
| 64 | case PixelFormat::R32G32_FLOAT: | ||
| 65 | case PixelFormat::R32G32_SINT: | ||
| 66 | case PixelFormat::R32_FLOAT: | ||
| 67 | case PixelFormat::R16_FLOAT: | ||
| 68 | case PixelFormat::R16_UNORM: | ||
| 69 | case PixelFormat::R16_SNORM: | ||
| 70 | case PixelFormat::R16_UINT: | ||
| 71 | case PixelFormat::R16_SINT: | ||
| 72 | case PixelFormat::R16G16_UNORM: | ||
| 73 | case PixelFormat::R16G16_FLOAT: | ||
| 74 | case PixelFormat::R16G16_UINT: | ||
| 75 | case PixelFormat::R16G16_SINT: | ||
| 76 | case PixelFormat::R16G16_SNORM: | ||
| 77 | case PixelFormat::R8G8_UNORM: | ||
| 78 | case PixelFormat::R8G8_SNORM: | ||
| 79 | case PixelFormat::R8G8_SINT: | ||
| 80 | case PixelFormat::R8G8_UINT: | ||
| 81 | case PixelFormat::R32G32_UINT: | ||
| 82 | case PixelFormat::R32_UINT: | ||
| 83 | case PixelFormat::R32_SINT: | ||
| 84 | case PixelFormat::G4R4_UNORM: | ||
| 85 | // Depth formats | ||
| 86 | case PixelFormat::D32_FLOAT: | ||
| 87 | case PixelFormat::D16_UNORM: | ||
| 88 | // Stencil formats | ||
| 89 | case PixelFormat::S8_UINT: | ||
| 90 | // DepthStencil formats | ||
| 91 | case PixelFormat::D24_UNORM_S8_UINT: | ||
| 92 | case PixelFormat::S8_UINT_D24_UNORM: | ||
| 93 | case PixelFormat::D32_FLOAT_S8_UINT: | ||
| 94 | return false; | ||
| 95 | default: | ||
| 96 | return true; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 48 | } // namespace VideoCommon | 100 | } // namespace VideoCommon |
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h index a25ae1d4a..87549ffff 100644 --- a/src/video_core/texture_cache/image_view_base.h +++ b/src/video_core/texture_cache/image_view_base.h | |||
| @@ -33,6 +33,8 @@ struct ImageViewBase { | |||
| 33 | return type == ImageViewType::Buffer; | 33 | return type == ImageViewType::Buffer; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | [[nodiscard]] bool SupportsAnisotropy() const noexcept; | ||
| 37 | |||
| 36 | ImageId image_id{}; | 38 | ImageId image_id{}; |
| 37 | GPUVAddr gpu_addr = 0; | 39 | GPUVAddr gpu_addr = 0; |
| 38 | PixelFormat format{}; | 40 | PixelFormat format{}; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index c7f7448e9..4027d860b 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -222,30 +222,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) { | |||
| 222 | 222 | ||
| 223 | template <class P> | 223 | template <class P> |
| 224 | typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { | 224 | typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { |
| 225 | return &slot_samplers[GetGraphicsSamplerId(index)]; | ||
| 226 | } | ||
| 227 | |||
| 228 | template <class P> | ||
| 229 | typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { | ||
| 230 | return &slot_samplers[GetComputeSamplerId(index)]; | ||
| 231 | } | ||
| 232 | |||
| 233 | template <class P> | ||
| 234 | SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) { | ||
| 225 | if (index > channel_state->graphics_sampler_table.Limit()) { | 235 | if (index > channel_state->graphics_sampler_table.Limit()) { |
| 226 | LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); | 236 | LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); |
| 227 | return &slot_samplers[NULL_SAMPLER_ID]; | 237 | return NULL_SAMPLER_ID; |
| 228 | } | 238 | } |
| 229 | const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); | 239 | const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); |
| 230 | SamplerId& id = channel_state->graphics_sampler_ids[index]; | 240 | SamplerId& id = channel_state->graphics_sampler_ids[index]; |
| 231 | if (is_new) { | 241 | if (is_new) { |
| 232 | id = FindSampler(descriptor); | 242 | id = FindSampler(descriptor); |
| 233 | } | 243 | } |
| 234 | return &slot_samplers[id]; | 244 | return id; |
| 235 | } | 245 | } |
| 236 | 246 | ||
| 237 | template <class P> | 247 | template <class P> |
| 238 | typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { | 248 | SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) { |
| 239 | if (index > channel_state->compute_sampler_table.Limit()) { | 249 | if (index > channel_state->compute_sampler_table.Limit()) { |
| 240 | LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); | 250 | LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); |
| 241 | return &slot_samplers[NULL_SAMPLER_ID]; | 251 | return NULL_SAMPLER_ID; |
| 242 | } | 252 | } |
| 243 | const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); | 253 | const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); |
| 244 | SamplerId& id = channel_state->compute_sampler_ids[index]; | 254 | SamplerId& id = channel_state->compute_sampler_ids[index]; |
| 245 | if (is_new) { | 255 | if (is_new) { |
| 246 | id = FindSampler(descriptor); | 256 | id = FindSampler(descriptor); |
| 247 | } | 257 | } |
| 248 | return &slot_samplers[id]; | 258 | return id; |
| 259 | } | ||
| 260 | |||
| 261 | template <class P> | ||
| 262 | const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept { | ||
| 263 | return slot_samplers[id]; | ||
| 264 | } | ||
| 265 | |||
| 266 | template <class P> | ||
| 267 | typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept { | ||
| 268 | return slot_samplers[id]; | ||
| 249 | } | 269 | } |
| 250 | 270 | ||
| 251 | template <class P> | 271 | template <class P> |
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 3bfa92154..d96ddea9d 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h | |||
| @@ -159,6 +159,18 @@ public: | |||
| 159 | /// Get the sampler from the compute descriptor table in the specified index | 159 | /// Get the sampler from the compute descriptor table in the specified index |
| 160 | Sampler* GetComputeSampler(u32 index); | 160 | Sampler* GetComputeSampler(u32 index); |
| 161 | 161 | ||
| 162 | /// Get the sampler id from the graphics descriptor table in the specified index | ||
| 163 | SamplerId GetGraphicsSamplerId(u32 index); | ||
| 164 | |||
| 165 | /// Get the sampler id from the compute descriptor table in the specified index | ||
| 166 | SamplerId GetComputeSamplerId(u32 index); | ||
| 167 | |||
| 168 | /// Return a constant reference to the given sampler id | ||
| 169 | [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept; | ||
| 170 | |||
| 171 | /// Return a reference to the given sampler id | ||
| 172 | [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept; | ||
| 173 | |||
| 162 | /// Refresh the state for graphics image view and sampler descriptors | 174 | /// Refresh the state for graphics image view and sampler descriptors |
| 163 | void SynchronizeGraphicsDescriptors(); | 175 | void SynchronizeGraphicsDescriptors(); |
| 164 | 176 | ||
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index 4a80a59f9..d8b88d9bc 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp | |||
| @@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept { | |||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | float TSCEntry::MaxAnisotropy() const noexcept { | 64 | float TSCEntry::MaxAnisotropy() const noexcept { |
| 65 | if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) { | 65 | const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None; |
| 66 | const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256; | ||
| 67 | const bool is_bilinear_filter = min_filter == TextureFilter::Linear && | ||
| 68 | reduction_filter == SamplerReduction::WeightedAverage; | ||
| 69 | if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods || | ||
| 70 | !is_bilinear_filter || depth_compare_enabled)) { | ||
| 66 | return 1.0f; | 71 | return 1.0f; |
| 67 | } | 72 | } |
| 68 | const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); | 73 | const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); |