summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt2
-rw-r--r--externals/nx_tzdb/CMakeLists.txt96
-rw-r--r--externals/nx_tzdb/NxTzdbCreateHeader.cmake8
-rw-r--r--externals/nx_tzdb/include/nx_tzdb.h2
m---------externals/nx_tzdb/tzdb_to_nx0
-rw-r--r--src/android/app/build.gradle.kts5
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt73
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt47
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml55
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml28
-rw-r--r--src/android/app/src/main/res/values/arrays.xml11
-rw-r--r--src/android/app/src/main/res/values/strings.xml18
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp2
-rw-r--r--src/core/hle/kernel/k_thread.cpp15
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp44
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.h9
-rw-r--r--src/core/hle/service/nfc/common/device.cpp164
-rw-r--r--src/core/hle/service/nfc/common/device.h11
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp4
-rw-r--r--src/core/hle/service/nfc/mifare_result.h2
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp13
-rw-r--r--src/core/hle/service/nfc/nfc_result.h3
-rw-r--r--src/core/hle/service/nfc/nfc_types.h37
-rw-r--r--src/core/hle/service/nfp/nfp_types.h26
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp5
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp10
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_context.h6
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h11
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp51
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp52
-rw-r--r--src/video_core/texture_cache/image_view_base.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h30
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h12
-rw-r--r--src/video_core/textures/texture.cpp7
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
60option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF) 60option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
61 61
62option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
63
62CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) 64CMAKE_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
4set(NX_TZDB_VERSION "220816") 4set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
5set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") 5
6add_library(nx_tzdb INTERFACE)
7
8find_program(GIT git)
9find_program(GNU_MAKE make)
10find_program(GNU_DATE date)
6 11
12set(CAN_BUILD_NX_TZDB true)
13
14if (NOT GIT)
15 set(CAN_BUILD_NX_TZDB false)
16endif()
17if (NOT GNU_MAKE)
18 set(CAN_BUILD_NX_TZDB false)
19endif()
20if (NOT GNU_DATE)
21 set(CAN_BUILD_NX_TZDB false)
22endif()
23if (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)
28endif()
29
30set(NX_TZDB_VERSION "220816")
7set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") 31set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
8set(NX_TZDB_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
9 32
10set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") 33set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
34
35if ((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
12if (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})
51elseif (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}")
19endif() 56endif()
20 57
21add_library(nx_tzdb INTERFACE)
22target_include_directories(nx_tzdb 58target_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})
42endfunction() 78endfunction()
43 79
44CreateHeader(${NX_TZDB_DIR} base) 80CreateHeader(${NX_TZDB_ROMFS_DIR} base)
45CreateHeader(${NX_TZDB_DIR}/zoneinfo zoneinfo) 81CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo)
46CreateHeader(${NX_TZDB_DIR}/zoneinfo/Africa africa) 82CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa)
47CreateHeader(${NX_TZDB_DIR}/zoneinfo/America america) 83CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america)
48CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Argentina america_argentina) 84CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina)
49CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Indiana america_indiana) 85CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana)
50CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/Kentucky america_kentucky) 86CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky)
51CreateHeader(${NX_TZDB_DIR}/zoneinfo/America/North_Dakota america_north_dakota) 87CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota)
52CreateHeader(${NX_TZDB_DIR}/zoneinfo/Antartica antartica) 88CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica)
53CreateHeader(${NX_TZDB_DIR}/zoneinfo/Arctic arctic) 89CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic)
54CreateHeader(${NX_TZDB_DIR}/zoneinfo/Asia asia) 90CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia)
55CreateHeader(${NX_TZDB_DIR}/zoneinfo/Atlantic atlantic) 91CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic)
56CreateHeader(${NX_TZDB_DIR}/zoneinfo/Australia australia) 92CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia)
57CreateHeader(${NX_TZDB_DIR}/zoneinfo/Brazil brazil) 93CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil)
58CreateHeader(${NX_TZDB_DIR}/zoneinfo/Canada canada) 94CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada)
59CreateHeader(${NX_TZDB_DIR}/zoneinfo/Chile chile) 95CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile)
60CreateHeader(${NX_TZDB_DIR}/zoneinfo/Etc etc) 96CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc)
61CreateHeader(${NX_TZDB_DIR}/zoneinfo/Europe europe) 97CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe)
62CreateHeader(${NX_TZDB_DIR}/zoneinfo/Indian indian) 98CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian)
63CreateHeader(${NX_TZDB_DIR}/zoneinfo/Mexico mexico) 99CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico)
64CreateHeader(${NX_TZDB_DIR}/zoneinfo/Pacific pacific) 100CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific)
65CreateHeader(${NX_TZDB_DIR}/zoneinfo/US us) 101CreateHeader(${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
16set(FILE_DATA "") 16set(FILE_DATA "")
17foreach(ZONE_FILE ${FILE_LIST}) 17foreach(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 {
163tasks.getByPath("preBuild").dependsOn("ktlintCheck") 163tasks.getByPath("preBuild").dependsOn("ktlintCheck")
164 164
165ktlint { 165ktlint {
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
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7
8class HeaderSetting( 6class 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
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8 8
9class StringSingleChoiceSetting( 9class 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 {
15const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&> 15const 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
1319void KThread::DummyThreadBeginWait() { 1322void 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
1329void KThread::DummyThreadEndWait() { 1333void 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
1338void KThread::BeginWait(KThreadQueue* queue) { 1345void 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) {
74NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { 74NTAG215File 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) {
108EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { 108EncryptedNTAG215File 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
142u32 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
151HashSeed GetSeed(const NTAG215File& data) { 142HashSeed 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>;
24struct HashSeed { 24struct 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};
33static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); 31static_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
70EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data); 68EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
71 69
72/// Returns password needed to allow write access to protected memory
73u32 GetTagPassword(const TagUuid& uuid);
74
75// Generates Seed needed for key derivation 70// Generates Seed needed for key derivation
76HashSeed GetSeed(const NTAG215File& data); 71HashSeed 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
278Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters, 283Result 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,
315Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) { 341Result 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
1197Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { 1245Result 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
1209Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const { 1259Result 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
1265Result 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
1231Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) { 1290Result 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
1296Result 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
1331Result 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
1263Result NfcDevice::WriteNtf(std::span<const u8> data) { 1337Result 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);
12constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73); 12constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
13constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80); 13constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
14constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97); 14constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
15constexpr Result ResultReadError(ErrorModule::NFCMifare, 288); 15constexpr 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) {
142void NfcInterface::StartDetection(HLERequestContext& ctx) { 142void 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);
24constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); 24constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
25constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); 25constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
26constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); 26constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
27constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); 27constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178);
28constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); 28constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
29constexpr 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
37enum class TagType : u32 { 37enum 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
46enum class PackedTagType : u8 { 49enum 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
57enum class NfcProtocol : u32 { 62enum 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
72using UniqueSerialNumber = std::array<u8, 7>; 75using UniqueSerialNumber = std::array<u8, 10>;
73using UniqueSerialNumberExtension = std::array<u8, 3>;
74 76
75// This is nn::nfc::DeviceHandle 77// This is nn::nfc::DeviceHandle
76using DeviceHandle = u64; 78using DeviceHandle = u64;
@@ -78,7 +80,6 @@ using DeviceHandle = u64;
78// This is nn::nfc::TagInfo 80// This is nn::nfc::TagInfo
79struct TagInfo { 81struct 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
88using LockBytes = std::array<u8, 2>; 88using UuidPart = std::array<u8, 3>;
89using HashData = std::array<u8, 0x20>; 89using HashData = std::array<u8, 0x20>;
90using ApplicationArea = std::array<u8, 0xD8>; 90using ApplicationArea = std::array<u8, 0xD8>;
91using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; 91using 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
94using TagInfo = NFC::TagInfo; 94using TagInfo = NFC::TagInfo;
95 95
96struct NtagTagUuid {
97 UuidPart part1;
98 UuidPart part2;
99 u8 nintendo_id;
100};
101static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size");
102
96struct TagUuid { 103struct 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};
101static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); 109static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size");
102 110
103struct WriteDate { 111struct WriteDate {
104 u16 year; 112 u16 year;
@@ -231,7 +239,8 @@ struct EncryptedAmiiboFile {
231static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); 239static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
232 240
233struct NTAG215File { 241struct 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
266struct EncryptedNTAG215File { 274struct 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&
849static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, 849static 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
131void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { 125void 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
275template <typename Spec> 275template <typename Spec>
276void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 276void 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
24struct Context { 24struct 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 {
309public: 309public:
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
316private: 324private:
317 OGLSampler sampler; 325 OGLSampler sampler;
326 OGLSampler sampler_default_anisotropy;
318}; 327};
319 328
320class Framebuffer { 329class 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:
178inline void PushImageDescriptors(TextureCache& texture_cache, 178inline 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
572void BufferCacheRuntime::ReserveNullBuffer() { 572void 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) {
298template <typename Spec> 298template <typename Spec>
299void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 299void 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
100class PipelineCache : public VideoCommon::ShaderCache { 100class 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
1828Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, 1837Framebuffer::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
282private: 290private:
283 vk::Sampler sampler; 291 vk::Sampler sampler;
292 vk::Sampler sampler_default_anisotropy;
284}; 293};
285 294
286class Framebuffer { 295class 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
46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} 46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
47 47
48bool 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
223template <class P> 223template <class P>
224typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { 224typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
225 return &slot_samplers[GetGraphicsSamplerId(index)];
226}
227
228template <class P>
229typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
230 return &slot_samplers[GetComputeSamplerId(index)];
231}
232
233template <class P>
234SamplerId 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
237template <class P> 247template <class P>
238typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { 248SamplerId 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
261template <class P>
262const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
263 return slot_samplers[id];
264}
265
266template <class P>
267typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
268 return slot_samplers[id];
249} 269}
250 270
251template <class P> 271template <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
64float TSCEntry::MaxAnisotropy() const noexcept { 64float 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();