summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dist/72-yuzu-input.rules19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt8
-rw-r--r--src/common/settings.cpp8
-rw-r--r--src/common/settings.h14
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/debug.cpp5
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp134
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h6
-rw-r--r--src/core/file_sys/romfs.cpp83
-rw-r--r--src/core/file_sys/vfs_concat.cpp4
-rw-r--r--src/core/file_sys/vfs_concat.h2
-rw-r--r--src/core/file_sys/vfs_layered.cpp21
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp199
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h87
-rw-r--r--src/core/hle/service/hid/errors.h5
-rw-r--r--src/core/hle/service/hid/hid.cpp8
-rw-r--r--src/core/hle/service/hid/hid_server.cpp7
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp136
-rw-r--r--src/core/hle/service/hid/hid_system_server.h6
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp62
-rw-r--r--src/core/hle/service/hid/resource_manager.h27
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp1
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp9
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h1
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp32
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h21
-rw-r--r--src/video_core/texture_cache/texture_cache.h35
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h3
39 files changed, 815 insertions, 225 deletions
diff --git a/dist/72-yuzu-input.rules b/dist/72-yuzu-input.rules
new file mode 100644
index 000000000..d64f8b28d
--- /dev/null
+++ b/dist/72-yuzu-input.rules
@@ -0,0 +1,19 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4# Allow systemd-logind to manage user access to hidraw with this file
5# On most systems, this file should be installed to /etc/udev/rules.d/72-yuzu-input.rules
6# Consult your distro if this is not the case
7
8# Switch Pro Controller (USB/Bluetooth)
9KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess"
10KERNEL=="hidraw*", KERNELS=="*057e:2009*", MODE="0660", TAG+="uaccess"
11
12# Joy-Con L (Bluetooth)
13KERNEL=="hidraw*", KERNELS=="*057e:2006*", MODE="0660", TAG+="uaccess"
14
15# Joy-Con R (Bluetooth)
16KERNEL=="hidraw*", KERNELS=="*057e:2007*", MODE="0660", TAG+="uaccess"
17
18# Joy-Con Charging Grip (USB)
19KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="200e", MODE="0660", TAG+="uaccess"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
index dec2b7cf1..9fab88248 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
@@ -14,8 +14,10 @@ import org.yuzu.yuzu_emu.R
14import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding 14import org.yuzu.yuzu_emu.databinding.DialogAddFolderBinding
15import org.yuzu.yuzu_emu.model.GameDir 15import org.yuzu.yuzu_emu.model.GameDir
16import org.yuzu.yuzu_emu.model.GamesViewModel 16import org.yuzu.yuzu_emu.model.GamesViewModel
17import org.yuzu.yuzu_emu.model.HomeViewModel
17 18
18class AddGameFolderDialogFragment : DialogFragment() { 19class AddGameFolderDialogFragment : DialogFragment() {
20 private val homeViewModel: HomeViewModel by activityViewModels()
19 private val gamesViewModel: GamesViewModel by activityViewModels() 21 private val gamesViewModel: GamesViewModel by activityViewModels()
20 22
21 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 23 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@@ -30,6 +32,7 @@ class AddGameFolderDialogFragment : DialogFragment() {
30 .setTitle(R.string.add_game_folder) 32 .setTitle(R.string.add_game_folder)
31 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> 33 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
32 val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked) 34 val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
35 homeViewModel.setGamesDirSelected(true)
33 gamesViewModel.addFolder(newGameDir) 36 gamesViewModel.addFolder(newGameDir)
34 } 37 }
35 .setNegativeButton(android.R.string.cancel, null) 38 .setNegativeButton(android.R.string.cancel, null)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index c4277735d..eb5edaa10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.Manifest 6import android.Manifest
7import android.annotation.SuppressLint
7import android.content.Intent 8import android.content.Intent
8import android.os.Build 9import android.os.Build
9import android.os.Bundle 10import android.os.Bundle
@@ -75,6 +76,8 @@ class SetupFragment : Fragment() {
75 return binding.root 76 return binding.root
76 } 77 }
77 78
79 // This is using the correct scope, lint is just acting up
80 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
78 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 81 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
79 mainActivity = requireActivity() as MainActivity 82 mainActivity = requireActivity() as MainActivity
80 83
@@ -206,12 +209,24 @@ class SetupFragment : Fragment() {
206 ) 209 )
207 } 210 }
208 211
209 viewLifecycleOwner.lifecycleScope.launch { 212 viewLifecycleOwner.lifecycleScope.apply {
210 repeatOnLifecycle(Lifecycle.State.CREATED) { 213 launch {
211 homeViewModel.shouldPageForward.collect { 214 repeatOnLifecycle(Lifecycle.State.CREATED) {
212 if (it) { 215 homeViewModel.shouldPageForward.collect {
213 pageForward() 216 if (it) {
214 homeViewModel.setShouldPageForward(false) 217 pageForward()
218 homeViewModel.setShouldPageForward(false)
219 }
220 }
221 }
222 }
223 launch {
224 repeatOnLifecycle(Lifecycle.State.CREATED) {
225 homeViewModel.gamesDirSelected.collect {
226 if (it) {
227 gamesDirCallback.onStepCompleted()
228 homeViewModel.setGamesDirSelected(false)
229 }
215 } 230 }
216 } 231 }
217 } 232 }
@@ -339,7 +354,6 @@ class SetupFragment : Fragment() {
339 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result -> 354 registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
340 if (result != null) { 355 if (result != null) {
341 mainActivity.processGamesDir(result) 356 mainActivity.processGamesDir(result)
342 gamesDirCallback.onStepCompleted()
343 } 357 }
344 } 358 }
345 359
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 752d98c10..fd925235b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -133,7 +133,7 @@ class GamesViewModel : ViewModel() {
133 viewModelScope.launch { 133 viewModelScope.launch {
134 withContext(Dispatchers.IO) { 134 withContext(Dispatchers.IO) {
135 NativeConfig.addGameDir(gameDir) 135 NativeConfig.addGameDir(gameDir)
136 getGameDirs() 136 getGameDirs(true)
137 } 137 }
138 } 138 }
139 139
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
index 251b5a667..07e65b028 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt
@@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.model
6import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
7import kotlinx.coroutines.flow.MutableStateFlow 7import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow 8import kotlinx.coroutines.flow.StateFlow
9import kotlinx.coroutines.flow.asStateFlow
9 10
10class HomeViewModel : ViewModel() { 11class HomeViewModel : ViewModel() {
11 val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible 12 val navigationVisible: StateFlow<Pair<Boolean, Boolean>> get() = _navigationVisible
@@ -17,6 +18,9 @@ class HomeViewModel : ViewModel() {
17 val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward 18 val shouldPageForward: StateFlow<Boolean> get() = _shouldPageForward
18 private val _shouldPageForward = MutableStateFlow(false) 19 private val _shouldPageForward = MutableStateFlow(false)
19 20
21 private val _gamesDirSelected = MutableStateFlow(false)
22 val gamesDirSelected get() = _gamesDirSelected.asStateFlow()
23
20 var navigatedToSetup = false 24 var navigatedToSetup = false
21 25
22 fun setNavigationVisibility(visible: Boolean, animated: Boolean) { 26 fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -36,4 +40,8 @@ class HomeViewModel : ViewModel() {
36 fun setShouldPageForward(pageForward: Boolean) { 40 fun setShouldPageForward(pageForward: Boolean) {
37 _shouldPageForward.value = pageForward 41 _shouldPageForward.value = pageForward
38 } 42 }
43
44 fun setGamesDirSelected(selected: Boolean) {
45 _gamesDirSelected.value = selected
46 }
39} 47}
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 4666bd0a0..88f509ba7 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -160,12 +160,16 @@ static bool is_nce_enabled = false;
160 160
161void SetNceEnabled(bool is_39bit) { 161void SetNceEnabled(bool is_39bit) {
162 const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce; 162 const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
163 is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit; 163 if (is_nce_selected && !IsFastmemEnabled()) {
164 if (is_nce_selected && !is_nce_enabled) { 164 LOG_WARNING(Common, "Fastmem is required to natively execute code in a performant manner, "
165 "falling back to Dynarmic");
166 }
167 if (is_nce_selected && !is_39bit) {
165 LOG_WARNING( 168 LOG_WARNING(
166 Common, 169 Common,
167 "Program does not utilize 39-bit address space, unable to natively execute code"); 170 "Program does not utilize 39-bit address space, unable to natively execute code");
168 } 171 }
172 is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
169} 173}
170 174
171bool IsNceEnabled() { 175bool IsNceEnabled() {
diff --git a/src/common/settings.h b/src/common/settings.h
index 98341ad96..7dc18fffe 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -180,14 +180,20 @@ struct Values {
180 &use_speed_limit}; 180 &use_speed_limit};
181 181
182 // Cpu 182 // Cpu
183 SwitchableSetting<CpuBackend, true> cpu_backend{ 183 SwitchableSetting<CpuBackend, true> cpu_backend{linkage,
184 linkage, CpuBackend::Dynarmic, CpuBackend::Dynarmic,
185#ifdef HAS_NCE 184#ifdef HAS_NCE
186 CpuBackend::Nce, 185 CpuBackend::Nce,
187#else 186#else
188 CpuBackend::Dynarmic, 187 CpuBackend::Dynarmic,
189#endif 188#endif
190 "cpu_backend", Category::Cpu}; 189 CpuBackend::Dynarmic,
190#ifdef HAS_NCE
191 CpuBackend::Nce,
192#else
193 CpuBackend::Dynarmic,
194#endif
195 "cpu_backend",
196 Category::Cpu};
191 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto, 197 SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
192 CpuAccuracy::Auto, CpuAccuracy::Paranoid, 198 CpuAccuracy::Auto, CpuAccuracy::Paranoid,
193 "cpu_accuracy", Category::Cpu}; 199 "cpu_accuracy", Category::Cpu};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 05c103f51..27d636ed4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -549,6 +549,8 @@ add_library(core STATIC
549 hle/service/hid/xcd.cpp 549 hle/service/hid/xcd.cpp
550 hle/service/hid/xcd.h 550 hle/service/hid/xcd.h
551 hle/service/hid/errors.h 551 hle/service/hid/errors.h
552 hle/service/hid/controllers/applet_resource.cpp
553 hle/service/hid/controllers/applet_resource.h
552 hle/service/hid/controllers/console_six_axis.cpp 554 hle/service/hid/controllers/console_six_axis.cpp
553 hle/service/hid/controllers/console_six_axis.h 555 hle/service/hid/controllers/console_six_axis.h
554 hle/service/hid/controllers/controller_base.cpp 556 hle/service/hid/controllers/controller_base.cpp
diff --git a/src/core/arm/debug.cpp b/src/core/arm/debug.cpp
index 1fe37b8ee..af1c34bc3 100644
--- a/src/core/arm/debug.cpp
+++ b/src/core/arm/debug.cpp
@@ -282,6 +282,8 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
282 282
283 // Ignore leading directories. 283 // Ignore leading directories.
284 char* path_pointer = module_path.path.data(); 284 char* path_pointer = module_path.path.data();
285 char* path_end =
286 path_pointer + std::min(PathLengthMax, module_path.path_length);
285 287
286 for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) && 288 for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
287 module_path.path[i] != '\0'; 289 module_path.path[i] != '\0';
@@ -292,7 +294,8 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
292 } 294 }
293 295
294 // Insert output. 296 // Insert output.
295 modules.emplace(svc_mem_info.base_address, path_pointer); 297 modules.emplace(svc_mem_info.base_address,
298 std::string_view(path_pointer, path_end));
296 } 299 }
297 } 300 }
298 } 301 }
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index f1d3e4129..dd9cca103 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstring> 4#include <cstring>
5#include <span>
5#include <string_view> 6#include <string_view>
6#include "common/alignment.h" 7#include "common/alignment.h"
7#include "common/assert.h" 8#include "common/assert.h"
@@ -134,7 +135,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
134 135
135 child->size = child->source->GetSize(); 136 child->size = child->source->GetSize();
136 137
137 AddFile(parent, child); 138 AddFile(parent, std::move(child));
138 } 139 }
139 140
140 for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) { 141 for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
@@ -163,36 +164,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
163 164
164bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 165bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
165 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { 166 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
166 // Check whether it's already in the known directories.
167 const auto [it, is_new] = directories.emplace(dir_ctx->path, nullptr);
168 if (!is_new) {
169 return false;
170 }
171
172 // Add a new directory. 167 // Add a new directory.
173 num_dirs++; 168 num_dirs++;
174 dir_table_size += 169 dir_table_size +=
175 sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); 170 sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
176 dir_ctx->parent = parent_dir_ctx; 171 dir_ctx->parent = std::move(parent_dir_ctx);
177 it->second = dir_ctx; 172 directories.emplace_back(std::move(dir_ctx));
178 173
179 return true; 174 return true;
180} 175}
181 176
182bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 177bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
183 std::shared_ptr<RomFSBuildFileContext> file_ctx) { 178 std::shared_ptr<RomFSBuildFileContext> file_ctx) {
184 // Check whether it's already in the known files.
185 const auto [it, is_new] = files.emplace(file_ctx->path, nullptr);
186 if (!is_new) {
187 return false;
188 }
189
190 // Add a new file. 179 // Add a new file.
191 num_files++; 180 num_files++;
192 file_table_size += 181 file_table_size +=
193 sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); 182 sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
194 file_ctx->parent = parent_dir_ctx; 183 file_ctx->parent = std::move(parent_dir_ctx);
195 it->second = file_ctx; 184 files.emplace_back(std::move(file_ctx));
196 185
197 return true; 186 return true;
198} 187}
@@ -201,7 +190,7 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
201 : base(std::move(base_)), ext(std::move(ext_)) { 190 : base(std::move(base_)), ext(std::move(ext_)) {
202 root = std::make_shared<RomFSBuildDirectoryContext>(); 191 root = std::make_shared<RomFSBuildDirectoryContext>();
203 root->path = "\0"; 192 root->path = "\0";
204 directories.emplace(root->path, root); 193 directories.emplace_back(root);
205 num_dirs = 1; 194 num_dirs = 1;
206 dir_table_size = 0x18; 195 dir_table_size = 0x18;
207 196
@@ -210,28 +199,43 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
210 199
211RomFSBuildContext::~RomFSBuildContext() = default; 200RomFSBuildContext::~RomFSBuildContext() = default;
212 201
213std::multimap<u64, VirtualFile> RomFSBuildContext::Build() { 202std::vector<std::pair<u64, VirtualFile>> RomFSBuildContext::Build() {
214 const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs); 203 const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
215 const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files); 204 const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
216 dir_hash_table_size = 4 * dir_hash_table_entry_count; 205 dir_hash_table_size = 4 * dir_hash_table_entry_count;
217 file_hash_table_size = 4 * file_hash_table_entry_count; 206 file_hash_table_size = 4 * file_hash_table_entry_count;
218 207
219 // Assign metadata pointers 208 // Assign metadata pointers.
220 RomFSHeader header{}; 209 RomFSHeader header{};
221 210
222 std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY); 211 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
223 std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY); 212 dir_table_size);
224 213 u32* const dir_hash_table_pointer = reinterpret_cast<u32*>(metadata.data());
225 std::vector<u8> dir_table(dir_table_size); 214 u8* const dir_table_pointer = metadata.data() + dir_hash_table_size;
226 std::vector<u8> file_table(file_table_size); 215 u32* const file_hash_table_pointer =
227 216 reinterpret_cast<u32*>(metadata.data() + dir_hash_table_size + dir_table_size);
228 std::shared_ptr<RomFSBuildFileContext> cur_file; 217 u8* const file_table_pointer =
218 metadata.data() + dir_hash_table_size + dir_table_size + file_hash_table_size;
219
220 std::span<u32> dir_hash_table(dir_hash_table_pointer, dir_hash_table_entry_count);
221 std::span<u32> file_hash_table(file_hash_table_pointer, file_hash_table_entry_count);
222 std::span<u8> dir_table(dir_table_pointer, dir_table_size);
223 std::span<u8> file_table(file_table_pointer, file_table_size);
224
225 // Initialize hash tables.
226 std::memset(dir_hash_table.data(), 0xFF, dir_hash_table.size_bytes());
227 std::memset(file_hash_table.data(), 0xFF, file_hash_table.size_bytes());
228
229 // Sort tables by name.
230 std::sort(files.begin(), files.end(),
231 [](const auto& a, const auto& b) { return a->path < b->path; });
232 std::sort(directories.begin(), directories.end(),
233 [](const auto& a, const auto& b) { return a->path < b->path; });
229 234
230 // Determine file offsets. 235 // Determine file offsets.
231 u32 entry_offset = 0; 236 u32 entry_offset = 0;
232 std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr; 237 std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
233 for (const auto& it : files) { 238 for (const auto& cur_file : files) {
234 cur_file = it.second;
235 file_partition_size = Common::AlignUp(file_partition_size, 16); 239 file_partition_size = Common::AlignUp(file_partition_size, 16);
236 cur_file->offset = file_partition_size; 240 cur_file->offset = file_partition_size;
237 file_partition_size += cur_file->size; 241 file_partition_size += cur_file->size;
@@ -243,34 +247,48 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
243 } 247 }
244 // Assign deferred parent/sibling ownership. 248 // Assign deferred parent/sibling ownership.
245 for (auto it = files.rbegin(); it != files.rend(); ++it) { 249 for (auto it = files.rbegin(); it != files.rend(); ++it) {
246 cur_file = it->second; 250 auto& cur_file = *it;
247 cur_file->sibling = cur_file->parent->file; 251 cur_file->sibling = cur_file->parent->file;
248 cur_file->parent->file = cur_file; 252 cur_file->parent->file = cur_file;
249 } 253 }
250 254
251 std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
252
253 // Determine directory offsets. 255 // Determine directory offsets.
254 entry_offset = 0; 256 entry_offset = 0;
255 for (const auto& it : directories) { 257 for (const auto& cur_dir : directories) {
256 cur_dir = it.second;
257 cur_dir->entry_offset = entry_offset; 258 cur_dir->entry_offset = entry_offset;
258 entry_offset += 259 entry_offset +=
259 static_cast<u32>(sizeof(RomFSDirectoryEntry) + 260 static_cast<u32>(sizeof(RomFSDirectoryEntry) +
260 Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4)); 261 Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4));
261 } 262 }
262 // Assign deferred parent/sibling ownership. 263 // Assign deferred parent/sibling ownership.
263 for (auto it = directories.rbegin(); it->second != root; ++it) { 264 for (auto it = directories.rbegin(); (*it) != root; ++it) {
264 cur_dir = it->second; 265 auto& cur_dir = *it;
265 cur_dir->sibling = cur_dir->parent->child; 266 cur_dir->sibling = cur_dir->parent->child;
266 cur_dir->parent->child = cur_dir; 267 cur_dir->parent->child = cur_dir;
267 } 268 }
268 269
269 std::multimap<u64, VirtualFile> out; 270 // Create output map.
271 std::vector<std::pair<u64, VirtualFile>> out;
272 out.reserve(num_files + 2);
273
274 // Set header fields.
275 header.header_size = sizeof(RomFSHeader);
276 header.file_hash_table_size = file_hash_table_size;
277 header.file_table_size = file_table_size;
278 header.dir_hash_table_size = dir_hash_table_size;
279 header.dir_table_size = dir_table_size;
280 header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
281 header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
282 header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
283 header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
284 header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
285
286 std::vector<u8> header_data(sizeof(RomFSHeader));
287 std::memcpy(header_data.data(), &header, header_data.size());
288 out.emplace_back(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
270 289
271 // Populate file tables. 290 // Populate file tables.
272 for (const auto& it : files) { 291 for (const auto& cur_file : files) {
273 cur_file = it.second;
274 RomFSFileEntry cur_entry{}; 292 RomFSFileEntry cur_entry{};
275 293
276 cur_entry.parent = cur_file->parent->entry_offset; 294 cur_entry.parent = cur_file->parent->entry_offset;
@@ -287,7 +305,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
287 305
288 cur_entry.name_size = name_size; 306 cur_entry.name_size = name_size;
289 307
290 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source)); 308 out.emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
291 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry)); 309 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
292 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0, 310 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
293 Common::AlignUp(cur_entry.name_size, 4)); 311 Common::AlignUp(cur_entry.name_size, 4));
@@ -296,8 +314,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
296 } 314 }
297 315
298 // Populate dir tables. 316 // Populate dir tables.
299 for (const auto& it : directories) { 317 for (const auto& cur_dir : directories) {
300 cur_dir = it.second;
301 RomFSDirectoryEntry cur_entry{}; 318 RomFSDirectoryEntry cur_entry{};
302 319
303 cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset; 320 cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
@@ -323,34 +340,13 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
323 cur_dir->path.data() + cur_dir->cur_path_ofs, name_size); 340 cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
324 } 341 }
325 342
326 // Set header fields. 343 // Write metadata.
327 header.header_size = sizeof(RomFSHeader); 344 out.emplace_back(header.dir_hash_table_ofs,
328 header.file_hash_table_size = file_hash_table_size; 345 std::make_shared<VectorVfsFile>(std::move(metadata)));
329 header.file_table_size = file_table_size;
330 header.dir_hash_table_size = dir_hash_table_size;
331 header.dir_table_size = dir_table_size;
332 header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
333 header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
334 header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
335 header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
336 header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
337
338 std::vector<u8> header_data(sizeof(RomFSHeader));
339 std::memcpy(header_data.data(), &header, header_data.size());
340 out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
341 346
342 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size + 347 // Sort the output.
343 dir_table_size); 348 std::sort(out.begin(), out.end(),
344 std::size_t index = 0; 349 [](const auto& a, const auto& b) { return a.first < b.first; });
345 std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
346 index += dir_hash_table.size() * sizeof(u32);
347 std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
348 index += dir_table.size();
349 std::memcpy(metadata.data() + index, file_hash_table.data(),
350 file_hash_table.size() * sizeof(u32));
351 index += file_hash_table.size() * sizeof(u32);
352 std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
353 out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
354 350
355 return out; 351 return out;
356} 352}
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index 06e5d5a47..f387c79f1 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -22,14 +22,14 @@ public:
22 ~RomFSBuildContext(); 22 ~RomFSBuildContext();
23 23
24 // This finalizes the context. 24 // This finalizes the context.
25 std::multimap<u64, VirtualFile> Build(); 25 std::vector<std::pair<u64, VirtualFile>> Build();
26 26
27private: 27private:
28 VirtualDir base; 28 VirtualDir base;
29 VirtualDir ext; 29 VirtualDir ext;
30 std::shared_ptr<RomFSBuildDirectoryContext> root; 30 std::shared_ptr<RomFSBuildDirectoryContext> root;
31 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; 31 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> directories;
32 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; 32 std::vector<std::shared_ptr<RomFSBuildFileContext>> files;
33 u64 num_dirs = 0; 33 u64 num_dirs = 0;
34 u64 num_files = 0; 34 u64 num_files = 0;
35 u64 dir_table_size = 0; 35 u64 dir_table_size = 0;
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 6de2103a0..6182598ae 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -55,44 +55,68 @@ struct FileEntry {
55}; 55};
56static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); 56static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
57 57
58template <typename Entry> 58struct RomFSTraversalContext {
59std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) { 59 RomFSHeader header;
60 Entry entry{}; 60 VirtualFile file;
61 if (file->ReadObject(&entry, offset) != sizeof(Entry)) 61 std::vector<u8> directory_meta;
62 return {}; 62 std::vector<u8> file_meta;
63 std::string string(entry.name_length, '\0'); 63};
64 if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size()) 64
65template <typename EntryType, auto Member>
66std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, size_t offset) {
67 const size_t entry_end = offset + sizeof(EntryType);
68 const std::vector<u8>& vec = ctx.*Member;
69 const size_t size = vec.size();
70 const u8* data = vec.data();
71 EntryType entry{};
72
73 if (entry_end > size) {
65 return {}; 74 return {};
66 return {entry, string}; 75 }
76 std::memcpy(&entry, data + offset, sizeof(EntryType));
77
78 const size_t name_length = std::min(entry_end + entry.name_length, size) - entry_end;
79 std::string name(reinterpret_cast<const char*>(data + entry_end), name_length);
80
81 return {entry, std::move(name)};
82}
83
84std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
85 size_t directory_offset) {
86 return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
87}
88
89std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
90 size_t file_offset) {
91 return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
67} 92}
68 93
69void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, 94void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,
70 u32 this_file_offset, std::shared_ptr<VectorVfsDirectory>& parent) { 95 std::shared_ptr<VectorVfsDirectory>& parent) {
71 while (this_file_offset != ROMFS_ENTRY_EMPTY) { 96 while (this_file_offset != ROMFS_ENTRY_EMPTY) {
72 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); 97 auto entry = GetFileEntry(ctx, this_file_offset);
73 98
74 parent->AddFile(std::make_shared<OffsetVfsFile>( 99 parent->AddFile(std::make_shared<OffsetVfsFile>(ctx.file, entry.first.size,
75 file, entry.first.size, entry.first.offset + data_offset, entry.second)); 100 entry.first.offset + ctx.header.data_offset,
101 std::move(entry.second)));
76 102
77 this_file_offset = entry.first.sibling; 103 this_file_offset = entry.first.sibling;
78 } 104 }
79} 105}
80 106
81void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, 107void ProcessDirectory(const RomFSTraversalContext& ctx, u32 this_dir_offset,
82 std::size_t data_offset, u32 this_dir_offset,
83 std::shared_ptr<VectorVfsDirectory>& parent) { 108 std::shared_ptr<VectorVfsDirectory>& parent) {
84 while (this_dir_offset != ROMFS_ENTRY_EMPTY) { 109 while (this_dir_offset != ROMFS_ENTRY_EMPTY) {
85 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); 110 auto entry = GetDirectoryEntry(ctx, this_dir_offset);
86 auto current = std::make_shared<VectorVfsDirectory>( 111 auto current = std::make_shared<VectorVfsDirectory>(
87 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second); 112 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
88 113
89 if (entry.first.child_file != ROMFS_ENTRY_EMPTY) { 114 if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
90 ProcessFile(file, file_offset, data_offset, entry.first.child_file, current); 115 ProcessFile(ctx, entry.first.child_file, current);
91 } 116 }
92 117
93 if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) { 118 if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
94 ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir, 119 ProcessDirectory(ctx, entry.first.child_dir, current);
95 current);
96 } 120 }
97 121
98 parent->AddDirectory(current); 122 parent->AddDirectory(current);
@@ -107,22 +131,25 @@ VirtualDir ExtractRomFS(VirtualFile file) {
107 return root_container; 131 return root_container;
108 } 132 }
109 133
110 RomFSHeader header{}; 134 RomFSTraversalContext ctx{};
111 if (file->ReadObject(&header) != sizeof(RomFSHeader)) { 135
112 return root_container; 136 if (file->ReadObject(&ctx.header) != sizeof(RomFSHeader)) {
137 return nullptr;
113 } 138 }
114 139
115 if (header.header_size != sizeof(RomFSHeader)) { 140 if (ctx.header.header_size != sizeof(RomFSHeader)) {
116 return root_container; 141 return nullptr;
117 } 142 }
118 143
119 const u64 file_offset = header.file_meta.offset; 144 ctx.file = file;
120 const u64 dir_offset = header.directory_meta.offset; 145 ctx.directory_meta =
146 file->ReadBytes(ctx.header.directory_meta.size, ctx.header.directory_meta.offset);
147 ctx.file_meta = file->ReadBytes(ctx.header.file_meta.size, ctx.header.file_meta.offset);
121 148
122 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); 149 ProcessDirectory(ctx, 0, root_container);
123 150
124 if (auto root = root_container->GetSubdirectory(""); root) { 151 if (auto root = root_container->GetSubdirectory(""); root) {
125 return std::make_shared<CachedVfsDirectory>(std::move(root)); 152 return root;
126 } 153 }
127 154
128 ASSERT(false); 155 ASSERT(false);
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 168b9cbec..7c7298527 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -59,8 +59,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
59 return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); 59 return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
60} 60}
61 61
62VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name, 62VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(
63 std::multimap<u64, VirtualFile>&& files) { 63 u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) {
64 // Fold trivial cases. 64 // Fold trivial cases.
65 if (files.empty()) { 65 if (files.empty()) {
66 return nullptr; 66 return nullptr;
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index cbddd12bd..b5f3d72e3 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -37,7 +37,7 @@ public:
37 /// Convenience function that turns a map of offsets to files into a concatenated file, filling 37 /// Convenience function that turns a map of offsets to files into a concatenated file, filling
38 /// gaps with a given filler byte. 38 /// gaps with a given filler byte.
39 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, 39 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
40 std::multimap<u64, VirtualFile>&& files); 40 std::vector<std::pair<u64, VirtualFile>>&& files);
41 41
42 std::string GetName() const override; 42 std::string GetName() const override;
43 std::size_t GetSize() const override; 43 std::size_t GetSize() const override;
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index 08daca397..5551743fb 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -3,6 +3,7 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <set> 5#include <set>
6#include <unordered_set>
6#include <utility> 7#include <utility>
7#include "core/file_sys/vfs_layered.h" 8#include "core/file_sys/vfs_layered.h"
8 9
@@ -59,13 +60,12 @@ std::string LayeredVfsDirectory::GetFullPath() const {
59 60
60std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { 61std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
61 std::vector<VirtualFile> out; 62 std::vector<VirtualFile> out;
62 std::set<std::string, std::less<>> out_names; 63 std::unordered_set<std::string> out_names;
63 64
64 for (const auto& layer : dirs) { 65 for (const auto& layer : dirs) {
65 for (auto& file : layer->GetFiles()) { 66 for (auto& file : layer->GetFiles()) {
66 auto file_name = file->GetName(); 67 const auto [it, is_new] = out_names.emplace(file->GetName());
67 if (!out_names.contains(file_name)) { 68 if (is_new) {
68 out_names.emplace(std::move(file_name));
69 out.emplace_back(std::move(file)); 69 out.emplace_back(std::move(file));
70 } 70 }
71 } 71 }
@@ -75,18 +75,19 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
75} 75}
76 76
77std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const { 77std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
78 std::vector<std::string> names; 78 std::vector<VirtualDir> out;
79 std::unordered_set<std::string> out_names;
80
79 for (const auto& layer : dirs) { 81 for (const auto& layer : dirs) {
80 for (const auto& sd : layer->GetSubdirectories()) { 82 for (const auto& sd : layer->GetSubdirectories()) {
81 if (std::find(names.begin(), names.end(), sd->GetName()) == names.end()) 83 out_names.emplace(sd->GetName());
82 names.push_back(sd->GetName());
83 } 84 }
84 } 85 }
85 86
86 std::vector<VirtualDir> out; 87 out.reserve(out_names.size());
87 out.reserve(names.size()); 88 for (const auto& subdir : out_names) {
88 for (const auto& subdir : names)
89 out.emplace_back(GetSubdirectory(subdir)); 89 out.emplace_back(GetSubdirectory(subdir));
90 }
90 91
91 return out; 92 return out;
92} 93}
diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp
new file mode 100644
index 000000000..ee60d8b44
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/applet_resource.cpp
@@ -0,0 +1,199 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/errors.h"
8
9namespace Service::HID {
10
11AppletResource::AppletResource(Core::System& system_) : system{system_} {}
12
13AppletResource::~AppletResource() = default;
14
15Result AppletResource::CreateAppletResource(u64 aruid) {
16 const u64 index = GetIndexFromAruid(aruid);
17
18 if (index >= AruidIndexMax) {
19 return ResultAruidNotRegistered;
20 }
21
22 if (data[index].flag.is_assigned) {
23 return ResultAruidAlreadyRegistered;
24 }
25
26 // TODO: Here shared memory is created for the process we don't quite emulate this part so
27 // obtain this pointer from system
28 auto& shared_memory = system.Kernel().GetHidSharedMem();
29
30 data[index].shared_memory_handle = &shared_memory;
31 data[index].flag.is_assigned.Assign(true);
32 // TODO: InitializeSixAxisControllerConfig(false);
33 active_aruid = aruid;
34 return ResultSuccess;
35}
36
37Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input) {
38 const u64 index = GetIndexFromAruid(aruid);
39
40 if (index < AruidIndexMax) {
41 return ResultAruidAlreadyRegistered;
42 }
43
44 std::size_t data_index = AruidIndexMax;
45 for (std::size_t i = 0; i < AruidIndexMax; i++) {
46 if (!data[i].flag.is_initialized) {
47 data_index = i;
48 break;
49 }
50 }
51
52 if (data_index == AruidIndexMax) {
53 return ResultAruidNoAvailableEntries;
54 }
55
56 AruidData& aruid_data = data[data_index];
57
58 aruid_data.aruid = aruid;
59 aruid_data.flag.is_initialized.Assign(true);
60 if (enable_input) {
61 aruid_data.flag.enable_pad_input.Assign(true);
62 aruid_data.flag.enable_six_axis_sensor.Assign(true);
63 aruid_data.flag.bit_18.Assign(true);
64 aruid_data.flag.enable_touchscreen.Assign(true);
65 }
66
67 data_index = AruidIndexMax;
68 for (std::size_t i = 0; i < AruidIndexMax; i++) {
69 if (registration_list.flag[i] == RegistrationStatus::Initialized) {
70 if (registration_list.aruid[i] != aruid) {
71 continue;
72 }
73 data_index = i;
74 break;
75 }
76 if (registration_list.flag[i] == RegistrationStatus::None) {
77 data_index = i;
78 break;
79 }
80 }
81
82 if (data_index == AruidIndexMax) {
83 return ResultSuccess;
84 }
85
86 registration_list.flag[data_index] = RegistrationStatus::Initialized;
87 registration_list.aruid[data_index] = aruid;
88
89 return ResultSuccess;
90}
91
92void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
93 u64 index = GetIndexFromAruid(aruid);
94
95 if (index < AruidIndexMax) {
96 if (data[index].flag.is_assigned) {
97 data[index].shared_memory_handle = nullptr;
98 data[index].flag.is_assigned.Assign(false);
99 }
100 }
101
102 index = GetIndexFromAruid(aruid);
103 if (index < AruidIndexMax) {
104 DestroySevenSixAxisTransferMemory();
105 data[index].flag.raw = 0;
106 data[index].aruid = 0;
107
108 index = GetIndexFromAruid(aruid);
109 if (index < AruidIndexMax) {
110 registration_list.flag[index] = RegistrationStatus::PendingDelete;
111 }
112 }
113}
114
115u64 AppletResource::GetActiveAruid() {
116 return active_aruid;
117}
118
119Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
120 u64 index = GetIndexFromAruid(aruid);
121 if (index >= AruidIndexMax) {
122 return ResultAruidNotRegistered;
123 }
124
125 *out_handle = data[index].shared_memory_handle;
126 return ResultSuccess;
127}
128
129u64 AppletResource::GetIndexFromAruid(u64 aruid) {
130 for (std::size_t i = 0; i < AruidIndexMax; i++) {
131 if (registration_list.flag[i] == RegistrationStatus::Initialized &&
132 registration_list.aruid[i] == aruid) {
133 return i;
134 }
135 }
136 return AruidIndexMax;
137}
138
139Result AppletResource::DestroySevenSixAxisTransferMemory() {
140 // TODO
141 return ResultSuccess;
142}
143
144void AppletResource::EnableInput(u64 aruid, bool is_enabled) {
145 const u64 index = GetIndexFromAruid(aruid);
146 if (index >= AruidIndexMax) {
147 return;
148 }
149
150 data[index].flag.enable_pad_input.Assign(is_enabled);
151 data[index].flag.enable_touchscreen.Assign(is_enabled);
152}
153
154void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
155 const u64 index = GetIndexFromAruid(aruid);
156 if (index >= AruidIndexMax) {
157 return;
158 }
159
160 data[index].flag.enable_six_axis_sensor.Assign(is_enabled);
161}
162
163void AppletResource::EnablePadInput(u64 aruid, bool is_enabled) {
164 const u64 index = GetIndexFromAruid(aruid);
165 if (index >= AruidIndexMax) {
166 return;
167 }
168
169 data[index].flag.enable_pad_input.Assign(is_enabled);
170}
171
172void AppletResource::EnableTouchScreen(u64 aruid, bool is_enabled) {
173 const u64 index = GetIndexFromAruid(aruid);
174 if (index >= AruidIndexMax) {
175 return;
176 }
177
178 data[index].flag.enable_touchscreen.Assign(is_enabled);
179}
180
181void AppletResource::SetIsPalmaConnectable(u64 aruid, bool is_connectable) {
182 const u64 index = GetIndexFromAruid(aruid);
183 if (index >= AruidIndexMax) {
184 return;
185 }
186
187 data[index].flag.is_palma_connectable.Assign(is_connectable);
188}
189
190void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) {
191 const u64 index = GetIndexFromAruid(aruid);
192 if (index >= AruidIndexMax) {
193 return;
194 }
195
196 data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
197}
198
199} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h
new file mode 100644
index 000000000..3dcec2898
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/applet_resource.h
@@ -0,0 +1,87 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_types.h"
10#include "core/hle/result.h"
11
12namespace Core {
13class System;
14}
15
16namespace Kernel {
17class KSharedMemory;
18}
19
20namespace Service::HID {
21class AppletResource {
22public:
23 explicit AppletResource(Core::System& system_);
24 ~AppletResource();
25
26 Result CreateAppletResource(u64 aruid);
27
28 Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
29 void UnregisterAppletResourceUserId(u64 aruid);
30
31 u64 GetActiveAruid();
32 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
33
34 u64 GetIndexFromAruid(u64 aruid);
35
36 Result DestroySevenSixAxisTransferMemory();
37
38 void EnableInput(u64 aruid, bool is_enabled);
39 void EnableSixAxisSensor(u64 aruid, bool is_enabled);
40 void EnablePadInput(u64 aruid, bool is_enabled);
41 void EnableTouchScreen(u64 aruid, bool is_enabled);
42 void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
43 void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
44
45private:
46 static constexpr std::size_t AruidIndexMax = 0x20;
47
48 enum RegistrationStatus : u32 {
49 None,
50 Initialized,
51 PendingDelete,
52 };
53
54 struct DataStatusFlag {
55 union {
56 u32 raw{};
57
58 BitField<0, 1, u32> is_initialized;
59 BitField<1, 1, u32> is_assigned;
60 BitField<16, 1, u32> enable_pad_input;
61 BitField<17, 1, u32> enable_six_axis_sensor;
62 BitField<18, 1, u32> bit_18;
63 BitField<19, 1, u32> is_palma_connectable;
64 BitField<20, 1, u32> enable_palma_boost_mode;
65 BitField<21, 1, u32> enable_touchscreen;
66 };
67 };
68
69 struct AruidRegisterList {
70 std::array<RegistrationStatus, AruidIndexMax> flag{};
71 std::array<u64, AruidIndexMax> aruid{};
72 };
73 static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
74
75 struct AruidData {
76 DataStatusFlag flag{};
77 u64 aruid{};
78 Kernel::KSharedMemory* shared_memory_handle{nullptr};
79 };
80
81 u64 active_aruid{};
82 AruidRegisterList registration_list{};
83 std::array<AruidData, AruidIndexMax> data{};
84
85 Core::System& system;
86};
87} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 9585bdaf0..f00cb831f 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -19,6 +19,11 @@ constexpr Result NpadIsSameType{ErrorModule::HID, 602};
19constexpr Result InvalidNpadId{ErrorModule::HID, 709}; 19constexpr Result InvalidNpadId{ErrorModule::HID, 709};
20constexpr Result NpadNotConnected{ErrorModule::HID, 710}; 20constexpr Result NpadNotConnected{ErrorModule::HID, 710};
21constexpr Result InvalidArraySize{ErrorModule::HID, 715}; 21constexpr Result InvalidArraySize{ErrorModule::HID, 715};
22
23constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
24constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
25constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};
26
22constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; 27constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
23 28
24} // namespace Service::HID 29} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1b7381d8d..afbcb019f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/kernel/k_process.h"
5#include "core/hle/kernel/kernel.h"
4#include "core/hle/service/hid/hid.h" 6#include "core/hle/service/hid/hid.h"
5#include "core/hle/service/hid/hid_debug_server.h" 7#include "core/hle/service/hid/hid_debug_server.h"
6#include "core/hle/service/hid/hid_firmware_settings.h" 8#include "core/hle/service/hid/hid_firmware_settings.h"
@@ -20,6 +22,12 @@ void LoopProcess(Core::System& system) {
20 std::shared_ptr<HidFirmwareSettings> firmware_settings = 22 std::shared_ptr<HidFirmwareSettings> firmware_settings =
21 std::make_shared<HidFirmwareSettings>(); 23 std::make_shared<HidFirmwareSettings>();
22 24
25 // TODO: Remove this hack until this service is emulated properly.
26 const auto process_list = system.Kernel().GetProcessList();
27 if (!process_list.empty()) {
28 resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
29 }
30
23 server_manager->RegisterNamedService( 31 server_manager->RegisterNamedService(
24 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings)); 32 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
25 server_manager->RegisterNamedService( 33 server_manager->RegisterNamedService(
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index a7d1578d9..e0f4051aa 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -224,8 +224,13 @@ void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
224 224
225 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 225 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
226 226
227 Result result = GetResourceManager()->CreateAppletResource(applet_resource_user_id);
228 if (result.IsSuccess()) {
229 result = GetResourceManager()->GetNpad()->Activate(applet_resource_user_id);
230 }
231
227 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 232 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
228 rb.Push(ResultSuccess); 233 rb.Push(result);
229 rb.PushIpcInterface<IAppletResource>(system, resource_manager); 234 rb.PushIpcInterface<IAppletResource>(system, resource_manager);
230} 235}
231 236
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index b56d0347a..4d33456a3 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -3,6 +3,7 @@
3 3
4#include "core/hid/hid_core.h" 4#include "core/hid/hid_core.h"
5#include "core/hle/service/hid/controllers/npad.h" 5#include "core/hle/service/hid/controllers/npad.h"
6#include "core/hle/service/hid/controllers/palma.h"
6#include "core/hle/service/hid/controllers/touchscreen.h" 7#include "core/hle/service/hid/controllers/touchscreen.h"
7#include "core/hle/service/hid/errors.h" 8#include "core/hle/service/hid/errors.h"
8#include "core/hle/service/hid/hid_system_server.h" 9#include "core/hle/service/hid/hid_system_server.h"
@@ -63,13 +64,13 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
63 {329, nullptr, "DetachAbstractedPadAll"}, 64 {329, nullptr, "DetachAbstractedPadAll"},
64 {330, nullptr, "CheckAbstractedPadConnection"}, 65 {330, nullptr, "CheckAbstractedPadConnection"},
65 {500, nullptr, "SetAppletResourceUserId"}, 66 {500, nullptr, "SetAppletResourceUserId"},
66 {501, nullptr, "RegisterAppletResourceUserId"}, 67 {501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
67 {502, nullptr, "UnregisterAppletResourceUserId"}, 68 {502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
68 {503, nullptr, "EnableAppletToGetInput"}, 69 {503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
69 {504, nullptr, "SetAruidValidForVibration"}, 70 {504, nullptr, "SetAruidValidForVibration"},
70 {505, nullptr, "EnableAppletToGetSixAxisSensor"}, 71 {505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
71 {506, nullptr, "EnableAppletToGetPadInput"}, 72 {506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
72 {507, nullptr, "EnableAppletToGetTouchScreen"}, 73 {507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
73 {510, nullptr, "SetVibrationMasterVolume"}, 74 {510, nullptr, "SetVibrationMasterVolume"},
74 {511, nullptr, "GetVibrationMasterVolume"}, 75 {511, nullptr, "GetVibrationMasterVolume"},
75 {512, nullptr, "BeginPermitVibrationSession"}, 76 {512, nullptr, "BeginPermitVibrationSession"},
@@ -420,6 +421,129 @@ void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) {
420 IPC::ResponseBuilder rb{ctx, 2}; 421 IPC::ResponseBuilder rb{ctx, 2};
421 rb.Push(ResultSuccess); 422 rb.Push(ResultSuccess);
422} 423}
424void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) {
425 IPC::RequestParser rp{ctx};
426 struct Parameters {
427 bool enable_input;
428 INSERT_PADDING_WORDS_NOINIT(1);
429 u64 applet_resource_user_id;
430 };
431 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
432
433 const auto parameters{rp.PopRaw<Parameters>()};
434
435 LOG_INFO(Service_HID, "called, enable_input={}, applet_resource_user_id={}",
436 parameters.enable_input, parameters.applet_resource_user_id);
437
438 Result result = GetResourceManager()->RegisterAppletResourceUserId(
439 parameters.applet_resource_user_id, parameters.enable_input);
440
441 if (result.IsSuccess()) {
442 // result = GetResourceManager()->GetNpad()->RegisterAppletResourceUserId(
443 // parameters.applet_resource_user_id);
444 }
445
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ResultSuccess);
448}
449
450void IHidSystemServer::UnregisterAppletResourceUserId(HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 u64 applet_resource_user_id{rp.Pop<u64>()};
453
454 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
455
456 GetResourceManager()->UnregisterAppletResourceUserId(applet_resource_user_id);
457 // GetResourceManager()->GetNpad()->UnregisterAppletResourceUserId(applet_resource_user_id);
458 // GetResourceManager()->GetPalma()->UnregisterAppletResourceUserId(applet_resource_user_id);
459
460 IPC::ResponseBuilder rb{ctx, 2};
461 rb.Push(ResultSuccess);
462}
463
464void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) {
465 IPC::RequestParser rp{ctx};
466 struct Parameters {
467 bool is_enabled;
468 INSERT_PADDING_WORDS_NOINIT(1);
469 u64 applet_resource_user_id;
470 };
471 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
472
473 const auto parameters{rp.PopRaw<Parameters>()};
474
475 LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
476 parameters.is_enabled, parameters.applet_resource_user_id);
477
478 GetResourceManager()->EnableInput(parameters.applet_resource_user_id, parameters.is_enabled);
479 // GetResourceManager()->GetNpad()->EnableInput(parameters.applet_resource_user_id);
480
481 IPC::ResponseBuilder rb{ctx, 2};
482 rb.Push(ResultSuccess);
483}
484
485void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
486 IPC::RequestParser rp{ctx};
487 struct Parameters {
488 bool is_enabled;
489 INSERT_PADDING_WORDS_NOINIT(1);
490 u64 applet_resource_user_id;
491 };
492 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
493
494 const auto parameters{rp.PopRaw<Parameters>()};
495
496 LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
497 parameters.is_enabled, parameters.applet_resource_user_id);
498
499 GetResourceManager()->EnableTouchScreen(parameters.applet_resource_user_id,
500 parameters.is_enabled);
501
502 IPC::ResponseBuilder rb{ctx, 2};
503 rb.Push(ResultSuccess);
504}
505
506void IHidSystemServer::EnableAppletToGetPadInput(HLERequestContext& ctx) {
507 IPC::RequestParser rp{ctx};
508 struct Parameters {
509 bool is_enabled;
510 INSERT_PADDING_WORDS_NOINIT(1);
511 u64 applet_resource_user_id;
512 };
513 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
514
515 const auto parameters{rp.PopRaw<Parameters>()};
516
517 LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
518 parameters.is_enabled, parameters.applet_resource_user_id);
519
520 GetResourceManager()->EnablePadInput(parameters.applet_resource_user_id, parameters.is_enabled);
521 // GetResourceManager()->GetNpad()->EnableInput(parameters.applet_resource_user_id);
522
523 IPC::ResponseBuilder rb{ctx, 2};
524 rb.Push(ResultSuccess);
525}
526
527void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
528 IPC::RequestParser rp{ctx};
529 struct Parameters {
530 bool is_enabled;
531 INSERT_PADDING_WORDS_NOINIT(1);
532 u64 applet_resource_user_id;
533 };
534 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
535
536 const auto parameters{rp.PopRaw<Parameters>()};
537
538 LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
539 parameters.is_enabled, parameters.applet_resource_user_id);
540
541 GetResourceManager()->EnableTouchScreen(parameters.applet_resource_user_id,
542 parameters.is_enabled);
543
544 IPC::ResponseBuilder rb{ctx, 2};
545 rb.Push(ResultSuccess);
546}
423 547
424void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { 548void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) {
425 LOG_INFO(Service_AM, "(STUBBED) called"); 549 LOG_INFO(Service_AM, "(STUBBED) called");
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 822d5e5b9..1e623dfc2 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -38,6 +38,12 @@ private:
38 void HasLeftRightBattery(HLERequestContext& ctx); 38 void HasLeftRightBattery(HLERequestContext& ctx);
39 void GetUniquePadsFromNpad(HLERequestContext& ctx); 39 void GetUniquePadsFromNpad(HLERequestContext& ctx);
40 void GetIrSensorState(HLERequestContext& ctx); 40 void GetIrSensorState(HLERequestContext& ctx);
41 void RegisterAppletResourceUserId(HLERequestContext& ctx);
42 void UnregisterAppletResourceUserId(HLERequestContext& ctx);
43 void EnableAppletToGetInput(HLERequestContext& ctx);
44 void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
45 void EnableAppletToGetPadInput(HLERequestContext& ctx);
46 void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
41 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); 47 void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
42 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); 48 void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
43 void GetRegisteredDevices(HLERequestContext& ctx); 49 void GetRegisteredDevices(HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index e76d4eea9..60d4ef71f 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -9,6 +9,7 @@
9#include "core/hle/service/hid/resource_manager.h" 9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
11 11
12#include "core/hle/service/hid/controllers/applet_resource.h"
12#include "core/hle/service/hid/controllers/console_six_axis.h" 13#include "core/hle/service/hid/controllers/console_six_axis.h"
13#include "core/hle/service/hid/controllers/debug_pad.h" 14#include "core/hle/service/hid/controllers/debug_pad.h"
14#include "core/hle/service/hid/controllers/gesture.h" 15#include "core/hle/service/hid/controllers/gesture.h"
@@ -33,7 +34,9 @@ constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 10
33constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) 34constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
34 35
35ResourceManager::ResourceManager(Core::System& system_) 36ResourceManager::ResourceManager(Core::System& system_)
36 : system{system_}, service_context{system_, "hid"} {} 37 : system{system_}, service_context{system_, "hid"} {
38 applet_resource = std::make_shared<AppletResource>(system);
39}
37 40
38ResourceManager::~ResourceManager() = default; 41ResourceManager::~ResourceManager() = default;
39 42
@@ -77,6 +80,11 @@ void ResourceManager::Initialize() {
77 system.HIDCore().ReloadInputDevices(); 80 system.HIDCore().ReloadInputDevices();
78 is_initialized = true; 81 is_initialized = true;
79} 82}
83
84std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
85 return applet_resource;
86}
87
80std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const { 88std::shared_ptr<CaptureButton> ResourceManager::GetCaptureButton() const {
81 return capture_button; 89 return capture_button;
82} 90}
@@ -137,6 +145,46 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
137 return unique_pad; 145 return unique_pad;
138} 146}
139 147
148Result ResourceManager::CreateAppletResource(u64 aruid) {
149 std::scoped_lock lock{shared_mutex};
150 return applet_resource->CreateAppletResource(aruid);
151}
152
153Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
154 std::scoped_lock lock{shared_mutex};
155 return applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
156}
157
158void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) {
159 std::scoped_lock lock{shared_mutex};
160 applet_resource->UnregisterAppletResourceUserId(aruid);
161}
162
163Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
164 std::scoped_lock lock{shared_mutex};
165 return applet_resource->GetSharedMemoryHandle(out_handle, aruid);
166}
167
168void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
169 std::scoped_lock lock{shared_mutex};
170 applet_resource->EnableInput(aruid, is_enabled);
171}
172
173void ResourceManager::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
174 std::scoped_lock lock{shared_mutex};
175 applet_resource->EnableSixAxisSensor(aruid, is_enabled);
176}
177
178void ResourceManager::EnablePadInput(u64 aruid, bool is_enabled) {
179 std::scoped_lock lock{shared_mutex};
180 applet_resource->EnablePadInput(aruid, is_enabled);
181}
182
183void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
184 std::scoped_lock lock{shared_mutex};
185 applet_resource->EnableTouchScreen(aruid, is_enabled);
186}
187
140void ResourceManager::UpdateControllers(std::uintptr_t user_data, 188void ResourceManager::UpdateControllers(std::uintptr_t user_data,
141 std::chrono::nanoseconds ns_late) { 189 std::chrono::nanoseconds ns_late) {
142 auto& core_timing = system.CoreTiming(); 190 auto& core_timing = system.CoreTiming();
@@ -172,14 +220,12 @@ void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose
172} 220}
173 221
174IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource) 222IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
175 : ServiceFramework{system_, "IAppletResource"} { 223 : ServiceFramework{system_, "IAppletResource"}, resource_manager{resource} {
176 static const FunctionInfo functions[] = { 224 static const FunctionInfo functions[] = {
177 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, 225 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
178 }; 226 };
179 RegisterHandlers(functions); 227 RegisterHandlers(functions);
180 228
181 resource->Initialize();
182
183 // Register update callbacks 229 // Register update callbacks
184 npad_update_event = Core::Timing::CreateEvent( 230 npad_update_event = Core::Timing::CreateEvent(
185 "HID::UpdatePadCallback", 231 "HID::UpdatePadCallback",
@@ -233,9 +279,13 @@ IAppletResource::~IAppletResource() {
233void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { 279void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
234 LOG_DEBUG(Service_HID, "called"); 280 LOG_DEBUG(Service_HID, "called");
235 281
282 Kernel::KSharedMemory* handle;
283 const u64 applet_resource_user_id = resource_manager->GetAppletResource()->GetActiveAruid();
284 const auto result = resource_manager->GetSharedMemoryHandle(&handle, applet_resource_user_id);
285
236 IPC::ResponseBuilder rb{ctx, 2, 1}; 286 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(ResultSuccess); 287 rb.Push(result);
238 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); 288 rb.PushCopyObjects(handle);
239} 289}
240 290
241} // namespace Service::HID 291} // namespace Service::HID
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 2b6a9b5e6..a78e2b729 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -6,11 +6,20 @@
6#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Core::Timing { 13namespace Core::Timing {
10struct EventType; 14struct EventType;
11} 15}
12 16
17namespace Kernel {
18class KSharedMemory;
19}
20
13namespace Service::HID { 21namespace Service::HID {
22class AppletResource;
14class Controller_Stubbed; 23class Controller_Stubbed;
15class ConsoleSixAxis; 24class ConsoleSixAxis;
16class DebugPad; 25class DebugPad;
@@ -38,6 +47,7 @@ public:
38 47
39 void Initialize(); 48 void Initialize();
40 49
50 std::shared_ptr<AppletResource> GetAppletResource() const;
41 std::shared_ptr<CaptureButton> GetCaptureButton() const; 51 std::shared_ptr<CaptureButton> GetCaptureButton() const;
42 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const; 52 std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
43 std::shared_ptr<DebugMouse> GetDebugMouse() const; 53 std::shared_ptr<DebugMouse> GetDebugMouse() const;
@@ -54,6 +64,18 @@ public:
54 std::shared_ptr<TouchScreen> GetTouchScreen() const; 64 std::shared_ptr<TouchScreen> GetTouchScreen() const;
55 std::shared_ptr<UniquePad> GetUniquePad() const; 65 std::shared_ptr<UniquePad> GetUniquePad() const;
56 66
67 Result CreateAppletResource(u64 aruid);
68
69 Result RegisterAppletResourceUserId(u64 aruid, bool bool_value);
70 void UnregisterAppletResourceUserId(u64 aruid);
71
72 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
73
74 void EnableInput(u64 aruid, bool is_enabled);
75 void EnableSixAxisSensor(u64 aruid, bool is_enabled);
76 void EnablePadInput(u64 aruid, bool is_enabled);
77 void EnableTouchScreen(u64 aruid, bool is_enabled);
78
57 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 79 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
58 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 80 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
59 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 81 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
@@ -62,6 +84,9 @@ public:
62private: 84private:
63 bool is_initialized{false}; 85 bool is_initialized{false};
64 86
87 mutable std::mutex shared_mutex;
88 std::shared_ptr<AppletResource> applet_resource = nullptr;
89
65 std::shared_ptr<CaptureButton> capture_button = nullptr; 90 std::shared_ptr<CaptureButton> capture_button = nullptr;
66 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; 91 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
67 std::shared_ptr<DebugMouse> debug_mouse = nullptr; 92 std::shared_ptr<DebugMouse> debug_mouse = nullptr;
@@ -106,6 +131,8 @@ private:
106 std::shared_ptr<Core::Timing::EventType> default_update_event; 131 std::shared_ptr<Core::Timing::EventType> default_update_event;
107 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; 132 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
108 std::shared_ptr<Core::Timing::EventType> motion_update_event; 133 std::shared_ptr<Core::Timing::EventType> motion_update_event;
134
135 std::shared_ptr<ResourceManager> resource_manager;
109}; 136};
110 137
111} // namespace Service::HID 138} // namespace Service::HID
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index d7db24f42..75bf31e32 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -171,6 +171,7 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
171 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
172 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
173 buffer->format = SharedBufferBlockLinearFormat; 173 buffer->format = SharedBufferBlockLinearFormat;
174 buffer->external_format = SharedBufferBlockLinearFormat;
174 buffer->buffer_id = handle; 175 buffer->buffer_id = handle;
175 buffer->offset = slot * SharedBufferSlotSize; 176 buffer->offset = slot * SharedBufferSlotSize;
176 ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); 177 ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
index dcd07e6d2..96686da59 100644
--- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -233,7 +233,12 @@ std::unique_ptr<Frame> DecoderContext::ReceiveFrame(bool* out_is_interlaced) {
233 return false; 233 return false;
234 } 234 }
235 235
236 *out_is_interlaced = frame->interlaced_frame != 0; 236 *out_is_interlaced =
237#if defined(FF_API_INTERLACED_FRAME) || LIBAVUTIL_VERSION_MAJOR >= 59
238 (frame->flags & AV_FRAME_FLAG_INTERLACED) != 0;
239#else
240 frame->interlaced_frame != 0;
241#endif
237 return true; 242 return true;
238 }; 243 };
239 244
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 9995b6dd4..279e5a4e0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -714,7 +714,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
714 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 714 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
715 715
716 std::scoped_lock lock{texture_cache.mutex}; 716 std::scoped_lock lock{texture_cache.mutex};
717 ImageView* const image_view{texture_cache.TryFindFramebufferImageView(framebuffer_addr)}; 717 ImageView* const image_view{
718 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr)};
718 if (!image_view) { 719 if (!image_view) {
719 return false; 720 return false;
720 } 721 }
@@ -725,7 +726,6 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
725 screen_info.texture.width = image_view->size.width; 726 screen_info.texture.width = image_view->size.width;
726 screen_info.texture.height = image_view->size.height; 727 screen_info.texture.height = image_view->size.height;
727 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); 728 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
728 screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format);
729 return true; 729 return true;
730} 730}
731 731
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 6bfed08a1..7a4f0c5c1 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -653,11 +653,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
653 }; 653 };
654 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); 654 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
655 655
656 if (screen_info.display_srgb) { 656 glDisable(GL_FRAMEBUFFER_SRGB);
657 glEnable(GL_FRAMEBUFFER_SRGB);
658 } else {
659 glDisable(GL_FRAMEBUFFER_SRGB);
660 }
661 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), 657 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
662 static_cast<GLfloat>(layout.height)); 658 static_cast<GLfloat>(layout.height));
663 659
@@ -710,8 +706,7 @@ void RendererOpenGL::RenderScreenshot() {
710 GLuint renderbuffer; 706 GLuint renderbuffer;
711 glGenRenderbuffers(1, &renderbuffer); 707 glGenRenderbuffers(1, &renderbuffer);
712 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); 708 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
713 glRenderbufferStorage(GL_RENDERBUFFER, screen_info.display_srgb ? GL_SRGB8 : GL_RGB8, 709 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
714 layout.width, layout.height);
715 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); 710 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
716 711
717 DrawScreen(layout); 712 DrawScreen(layout);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index f1d5fd954..b70607635 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -53,7 +53,6 @@ struct TextureInfo {
53struct ScreenInfo { 53struct ScreenInfo {
54 GLuint display_texture{}; 54 GLuint display_texture{};
55 bool was_accelerated = false; 55 bool was_accelerated = false;
56 bool display_srgb{};
57 const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f}; 56 const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
58 TextureInfo texture; 57 TextureInfo texture;
59}; 58};
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index c4c30d807..100b70918 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -94,7 +94,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
94 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), 94 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
95 scheduler(device, state_tracker), 95 scheduler(device, state_tracker),
96 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, 96 swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
97 render_window.GetFramebufferLayout().height, false), 97 render_window.GetFramebufferLayout().height),
98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
99 surface), 99 surface),
100 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager, 100 blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
@@ -131,11 +131,10 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
131 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 131 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
132 const bool use_accelerated = 132 const bool use_accelerated =
133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
134 const bool is_srgb = use_accelerated && screen_info.is_srgb;
135 RenderScreenshot(*framebuffer, use_accelerated); 134 RenderScreenshot(*framebuffer, use_accelerated);
136 135
137 Frame* frame = present_manager.GetRenderFrame(); 136 Frame* frame = present_manager.GetRenderFrame();
138 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); 137 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated);
139 scheduler.Flush(*frame->render_ready); 138 scheduler.Flush(*frame->render_ready);
140 present_manager.Present(frame); 139 present_manager.Present(frame);
141 140
@@ -205,7 +204,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
205 .flags = 0, 204 .flags = 0,
206 .image = *staging_image, 205 .image = *staging_image,
207 .viewType = VK_IMAGE_VIEW_TYPE_2D, 206 .viewType = VK_IMAGE_VIEW_TYPE_2D,
208 .format = screen_info.is_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM, 207 .format = VK_FORMAT_B8G8R8A8_UNORM,
209 .components{ 208 .components{
210 .r = VK_COMPONENT_SWIZZLE_IDENTITY, 209 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
211 .g = VK_COMPONENT_SWIZZLE_IDENTITY, 210 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 5e461fbd0..60432f5ad 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -127,9 +127,9 @@ BlitScreen::BlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWin
127 Scheduler& scheduler_, const ScreenInfo& screen_info_) 127 Scheduler& scheduler_, const ScreenInfo& screen_info_)
128 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_}, 128 : cpu_memory{cpu_memory_}, render_window{render_window_}, device{device_},
129 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_}, 129 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
130 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_}, 130 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
131 current_srgb{swapchain.IsSrgb()}, image_view_format{swapchain.GetImageViewFormat()} {
132 resource_ticks.resize(image_count); 131 resource_ticks.resize(image_count);
132 swapchain_view_format = swapchain.GetImageViewFormat();
133 133
134 CreateStaticResources(); 134 CreateStaticResources();
135 CreateDynamicResources(); 135 CreateDynamicResources();
@@ -480,28 +480,22 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
480} 480}
481 481
482void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, 482void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
483 bool use_accelerated, bool is_srgb) { 483 bool use_accelerated) {
484 // Recreate dynamic resources if the the image count or colorspace changed 484 // Recreate dynamic resources if the the image count or input format changed
485 const VkFormat current_framebuffer_format =
486 std::exchange(framebuffer_view_format, GetFormat(framebuffer));
485 if (const std::size_t swapchain_images = swapchain.GetImageCount(); 487 if (const std::size_t swapchain_images = swapchain.GetImageCount();
486 swapchain_images != image_count || current_srgb != is_srgb) { 488 swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) {
487 current_srgb = is_srgb;
488#ifdef ANDROID
489 // Android is already ordered the same as Switch.
490 image_view_format = current_srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
491#else
492 image_view_format = current_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
493#endif
494 image_count = swapchain_images; 489 image_count = swapchain_images;
495 Recreate(); 490 Recreate();
496 } 491 }
497 492
498 // Recreate the presentation frame if the dimensions of the window changed 493 // Recreate the presentation frame if the dimensions of the window changed
499 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 494 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
500 if (layout.width != frame->width || layout.height != frame->height || 495 if (layout.width != frame->width || layout.height != frame->height) {
501 is_srgb != frame->is_srgb) {
502 Recreate(); 496 Recreate();
503 present_manager.RecreateFrame(frame, layout.width, layout.height, is_srgb, 497 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
504 image_view_format, *renderpass); 498 *renderpass);
505 } 499 }
506 500
507 const VkExtent2D render_area{frame->width, frame->height}; 501 const VkExtent2D render_area{frame->width, frame->height};
@@ -629,7 +623,7 @@ void BlitScreen::CreateDescriptorPool() {
629} 623}
630 624
631void BlitScreen::CreateRenderPass() { 625void BlitScreen::CreateRenderPass() {
632 renderpass = CreateRenderPassImpl(image_view_format); 626 renderpass = CreateRenderPassImpl(swapchain_view_format);
633} 627}
634 628
635vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) { 629vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
@@ -1149,7 +1143,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1149 .pNext = nullptr, 1143 .pNext = nullptr,
1150 .flags = 0, 1144 .flags = 0,
1151 .imageType = VK_IMAGE_TYPE_2D, 1145 .imageType = VK_IMAGE_TYPE_2D,
1152 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer), 1146 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1153 .extent = 1147 .extent =
1154 { 1148 {
1155 .width = (up_scale * framebuffer.width) >> down_shift, 1149 .width = (up_scale * framebuffer.width) >> down_shift,
@@ -1174,7 +1168,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1174 .flags = 0, 1168 .flags = 0,
1175 .image = *image, 1169 .image = *image,
1176 .viewType = VK_IMAGE_VIEW_TYPE_2D, 1170 .viewType = VK_IMAGE_VIEW_TYPE_2D,
1177 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer), 1171 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1178 .components = 1172 .components =
1179 { 1173 {
1180 .r = VK_COMPONENT_SWIZZLE_IDENTITY, 1174 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 8365b5668..16b882b6d 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -52,7 +52,6 @@ struct ScreenInfo {
52 VkImageView image_view{}; 52 VkImageView image_view{};
53 u32 width{}; 53 u32 width{};
54 u32 height{}; 54 u32 height{};
55 bool is_srgb{};
56}; 55};
57 56
58class BlitScreen { 57class BlitScreen {
@@ -69,7 +68,7 @@ public:
69 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); 68 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated);
70 69
71 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, 70 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
72 bool use_accelerated, bool is_srgb); 71 bool use_accelerated);
73 72
74 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 73 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
75 VkExtent2D extent); 74 VkExtent2D extent);
@@ -161,8 +160,8 @@ private:
161 u32 raw_width = 0; 160 u32 raw_width = 0;
162 u32 raw_height = 0; 161 u32 raw_height = 0;
163 Service::android::PixelFormat pixel_format{}; 162 Service::android::PixelFormat pixel_format{};
164 bool current_srgb; 163 VkFormat framebuffer_view_format;
165 VkFormat image_view_format; 164 VkFormat swapchain_view_format;
166 165
167 std::unique_ptr<FSR> fsr; 166 std::unique_ptr<FSR> fsr;
168 std::unique_ptr<SMAA> smaa; 167 std::unique_ptr<SMAA> smaa;
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 2ef36583b..8e4c74b5c 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -172,13 +172,12 @@ void PresentManager::Present(Frame* frame) {
172 }); 172 });
173} 173}
174 174
175void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb, 175void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format,
176 VkFormat image_view_format, VkRenderPass rd) { 176 VkRenderPass rd) {
177 auto& dld = device.GetLogical(); 177 auto& dld = device.GetLogical();
178 178
179 frame->width = width; 179 frame->width = width;
180 frame->height = height; 180 frame->height = height;
181 frame->is_srgb = is_srgb;
182 181
183 frame->image = memory_allocator.CreateImage({ 182 frame->image = memory_allocator.CreateImage({
184 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 183 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -289,7 +288,7 @@ void PresentManager::PresentThread(std::stop_token token) {
289} 288}
290 289
291void PresentManager::RecreateSwapchain(Frame* frame) { 290void PresentManager::RecreateSwapchain(Frame* frame) {
292 swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb); 291 swapchain.Create(*surface, frame->width, frame->height);
293 image_count = swapchain.GetImageCount(); 292 image_count = swapchain.GetImageCount();
294} 293}
295 294
@@ -319,12 +318,12 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
319void PresentManager::CopyToSwapchainImpl(Frame* frame) { 318void PresentManager::CopyToSwapchainImpl(Frame* frame) {
320 MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain); 319 MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
321 320
322 // If the size or colorspace of the incoming frames has changed, recreate the swapchain 321 // If the size of the incoming frames has changed, recreate the swapchain
323 // to account for that. 322 // to account for that.
324 const bool srgb_changed = swapchain.NeedsRecreation(frame->is_srgb); 323 const bool is_suboptimal = swapchain.NeedsRecreation();
325 const bool size_changed = 324 const bool size_changed =
326 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; 325 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height;
327 if (srgb_changed || size_changed) { 326 if (is_suboptimal || size_changed) {
328 RecreateSwapchain(frame); 327 RecreateSwapchain(frame);
329 } 328 }
330 329
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.h b/src/video_core/renderer_vulkan/vk_present_manager.h
index a3d825fe6..337171a09 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.h
+++ b/src/video_core/renderer_vulkan/vk_present_manager.h
@@ -25,7 +25,6 @@ class Swapchain;
25struct Frame { 25struct Frame {
26 u32 width; 26 u32 width;
27 u32 height; 27 u32 height;
28 bool is_srgb;
29 vk::Image image; 28 vk::Image image;
30 vk::ImageView image_view; 29 vk::ImageView image_view;
31 vk::Framebuffer framebuffer; 30 vk::Framebuffer framebuffer;
@@ -48,8 +47,8 @@ public:
48 void Present(Frame* frame); 47 void Present(Frame* frame);
49 48
50 /// Recreates the present frame to match the provided parameters 49 /// Recreates the present frame to match the provided parameters
51 void RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb, 50 void RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format,
52 VkFormat image_view_format, VkRenderPass rd); 51 VkRenderPass rd);
53 52
54 /// Waits for the present thread to finish presenting all queued frames. 53 /// Waits for the present thread to finish presenting all queued frames.
55 void WaitPresent(); 54 void WaitPresent();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index b6f52e017..59829c88b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -783,7 +783,8 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
783 return false; 783 return false;
784 } 784 }
785 std::scoped_lock lock{texture_cache.mutex}; 785 std::scoped_lock lock{texture_cache.mutex};
786 ImageView* const image_view = texture_cache.TryFindFramebufferImageView(framebuffer_addr); 786 ImageView* const image_view =
787 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
787 if (!image_view) { 788 if (!image_view) {
788 return false; 789 return false;
789 } 790 }
@@ -792,7 +793,6 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
792 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); 793 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D);
793 screen_info.width = image_view->size.width; 794 screen_info.width = image_view->size.width;
794 screen_info.height = image_view->size.height; 795 screen_info.height = image_view->size.height;
795 screen_info.is_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format);
796 return true; 796 return true;
797} 797}
798 798
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 821f44f1a..86a30dcd1 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -105,14 +105,14 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
105} // Anonymous namespace 105} // Anonymous namespace
106 106
107Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, 107Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
108 u32 width_, u32 height_, bool srgb) 108 u32 width_, u32 height_)
109 : surface{surface_}, device{device_}, scheduler{scheduler_} { 109 : surface{surface_}, device{device_}, scheduler{scheduler_} {
110 Create(surface_, width_, height_, srgb); 110 Create(surface_, width_, height_);
111} 111}
112 112
113Swapchain::~Swapchain() = default; 113Swapchain::~Swapchain() = default;
114 114
115void Swapchain::Create(VkSurfaceKHR surface_, u32 width_, u32 height_, bool srgb) { 115void Swapchain::Create(VkSurfaceKHR surface_, u32 width_, u32 height_) {
116 is_outdated = false; 116 is_outdated = false;
117 is_suboptimal = false; 117 is_suboptimal = false;
118 width = width_; 118 width = width_;
@@ -127,7 +127,7 @@ void Swapchain::Create(VkSurfaceKHR surface_, u32 width_, u32 height_, bool srgb
127 127
128 Destroy(); 128 Destroy();
129 129
130 CreateSwapchain(capabilities, srgb); 130 CreateSwapchain(capabilities);
131 CreateSemaphores(); 131 CreateSemaphores();
132 132
133 resource_ticks.clear(); 133 resource_ticks.clear();
@@ -196,7 +196,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
196 } 196 }
197} 197}
198 198
199void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { 199void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
200 const auto physical_device{device.GetPhysical()}; 200 const auto physical_device{device.GetPhysical()};
201 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; 201 const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
202 const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface); 202 const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
@@ -274,15 +274,14 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
274 swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci); 274 swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);
275 275
276 extent = swapchain_ci.imageExtent; 276 extent = swapchain_ci.imageExtent;
277 current_srgb = srgb;
278 277
279 images = swapchain.GetImages(); 278 images = swapchain.GetImages();
280 image_count = static_cast<u32>(images.size()); 279 image_count = static_cast<u32>(images.size());
281#ifdef ANDROID 280#ifdef ANDROID
282 // Android is already ordered the same as Switch. 281 // Android is already ordered the same as Switch.
283 image_view_format = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; 282 image_view_format = VK_FORMAT_R8G8B8A8_UNORM;
284#else 283#else
285 image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; 284 image_view_format = VK_FORMAT_B8G8R8A8_UNORM;
286#endif 285#endif
287} 286}
288 287
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index b8a1465a6..d264f06e4 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -20,11 +20,11 @@ class Scheduler;
20class Swapchain { 20class Swapchain {
21public: 21public:
22 explicit Swapchain(VkSurfaceKHR surface, const Device& device, Scheduler& scheduler, u32 width, 22 explicit Swapchain(VkSurfaceKHR surface, const Device& device, Scheduler& scheduler, u32 width,
23 u32 height, bool srgb); 23 u32 height);
24 ~Swapchain(); 24 ~Swapchain();
25 25
26 /// Creates (or recreates) the swapchain with a given size. 26 /// Creates (or recreates) the swapchain with a given size.
27 void Create(VkSurfaceKHR surface, u32 width, u32 height, bool srgb); 27 void Create(VkSurfaceKHR surface, u32 width, u32 height);
28 28
29 /// Acquires the next image in the swapchain, waits as needed. 29 /// Acquires the next image in the swapchain, waits as needed.
30 bool AcquireNextImage(); 30 bool AcquireNextImage();
@@ -33,13 +33,8 @@ public:
33 void Present(VkSemaphore render_semaphore); 33 void Present(VkSemaphore render_semaphore);
34 34
35 /// Returns true when the swapchain needs to be recreated. 35 /// Returns true when the swapchain needs to be recreated.
36 bool NeedsRecreation(bool is_srgb) const { 36 bool NeedsRecreation() const {
37 return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate(); 37 return IsSubOptimal() || NeedsPresentModeUpdate();
38 }
39
40 /// Returns true when the color space has changed.
41 bool HasColorSpaceChanged(bool is_srgb) const {
42 return current_srgb != is_srgb;
43 } 38 }
44 39
45 /// Returns true when the swapchain is outdated. 40 /// Returns true when the swapchain is outdated.
@@ -52,11 +47,6 @@ public:
52 return is_suboptimal; 47 return is_suboptimal;
53 } 48 }
54 49
55 /// Returns true when the swapchain format is in the srgb color space
56 bool IsSrgb() const {
57 return current_srgb;
58 }
59
60 VkExtent2D GetSize() const { 50 VkExtent2D GetSize() const {
61 return extent; 51 return extent;
62 } 52 }
@@ -110,7 +100,7 @@ public:
110 } 100 }
111 101
112private: 102private:
113 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); 103 void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities);
114 void CreateSemaphores(); 104 void CreateSemaphores();
115 void CreateImageViews(); 105 void CreateImageViews();
116 106
@@ -144,7 +134,6 @@ private:
144 bool has_mailbox{false}; 134 bool has_mailbox{false};
145 bool has_fifo_relaxed{false}; 135 bool has_fifo_relaxed{false};
146 136
147 bool current_srgb{};
148 bool is_outdated{}; 137 bool is_outdated{};
149 bool is_suboptimal{}; 138 bool is_suboptimal{};
150}; 139};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index dade38b18..0d5a1709f 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -712,14 +712,15 @@ bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
712} 712}
713 713
714template <class P> 714template <class P>
715typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { 715typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(
716 const Tegra::FramebufferConfig& config, VAddr cpu_addr) {
716 // TODO: Properly implement this 717 // TODO: Properly implement this
717 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); 718 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS);
718 if (it == page_table.end()) { 719 if (it == page_table.end()) {
719 return nullptr; 720 return nullptr;
720 } 721 }
721 const auto& image_map_ids = it->second; 722 const auto& image_map_ids = it->second;
722 boost::container::small_vector<const ImageBase*, 4> valid_images; 723 boost::container::small_vector<ImageId, 4> valid_image_ids;
723 for (const ImageMapId map_id : image_map_ids) { 724 for (const ImageMapId map_id : image_map_ids) {
724 const ImageMapView& map = slot_map_views[map_id]; 725 const ImageMapView& map = slot_map_views[map_id];
725 const ImageBase& image = slot_images[map.image_id]; 726 const ImageBase& image = slot_images[map.image_id];
@@ -729,18 +730,34 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_ad
729 if (image.image_view_ids.empty()) { 730 if (image.image_view_ids.empty()) {
730 continue; 731 continue;
731 } 732 }
732 valid_images.push_back(&image); 733 valid_image_ids.push_back(map.image_id);
733 } 734 }
734 735
735 if (valid_images.size() == 1) [[likely]] { 736 const auto view_format = [&]() {
736 return &slot_image_views[valid_images[0]->image_view_ids.at(0)]; 737 switch (config.pixel_format) {
738 case Service::android::PixelFormat::Rgb565:
739 return PixelFormat::R5G6B5_UNORM;
740 case Service::android::PixelFormat::Bgra8888:
741 return PixelFormat::B8G8R8A8_UNORM;
742 default:
743 return PixelFormat::A8B8G8R8_UNORM;
744 }
745 }();
746
747 const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
748 const ImageViewInfo info{ImageViewType::e2D, view_format};
749 return &slot_image_views[FindOrEmplaceImageView(image_id, info)];
750 };
751
752 if (valid_image_ids.size() == 1) [[likely]] {
753 return GetImageViewForFramebuffer(valid_image_ids.front());
737 } 754 }
738 755
739 if (valid_images.size() > 0) [[unlikely]] { 756 if (valid_image_ids.size() > 0) [[unlikely]] {
740 std::ranges::sort(valid_images, [](const auto* a, const auto* b) { 757 auto most_recent = std::ranges::max_element(valid_image_ids, [&](auto a, auto b) {
741 return a->modification_tick > b->modification_tick; 758 return slot_images[a].modification_tick < slot_images[b].modification_tick;
742 }); 759 });
743 return &slot_image_views[valid_images[0]->image_view_ids.at(0)]; 760 return GetImageViewForFramebuffer(*most_recent);
744 } 761 }
745 762
746 return nullptr; 763 return nullptr;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index a40825c9f..cbe56e166 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -209,7 +209,8 @@ public:
209 const Tegra::Engines::Fermi2D::Config& copy); 209 const Tegra::Engines::Fermi2D::Config& copy);
210 210
211 /// Try to find a cached image view in the given CPU address 211 /// Try to find a cached image view in the given CPU address
212 [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr); 212 [[nodiscard]] ImageView* TryFindFramebufferImageView(const Tegra::FramebufferConfig& config,
213 VAddr cpu_addr);
213 214
214 /// Return true when there are uncommitted images to be downloaded 215 /// Return true when there are uncommitted images to be downloaded
215 [[nodiscard]] bool HasUncommittedFlushes() const noexcept; 216 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;